递归实现数组中元素的重排 - Swift

1. 题目要求:

对于给定的可能有重复元素的数组,打印出所有数组中所有元素重新排列的所有可能的不同组合,不要求各个组合之间的顺序
例如:
给出数组[1, 2]
打印结果:
1 2
2 1

2. 解题思路:

  • 取出数组中的第一个元素
  • 递归调用函数,将剩余元素进行重排,返回值类型为嵌套的数组
  • 将取出的元素插入到重排结果中的每一个数组中的每一个位置,依次生成本次递归返回的最终的元素组合之一
  • 利用哈希算法去重后,将不重复的元素组合添加到最终要返回的数组中
  • 关于去重的算法,可以利用Set中元素查找过程利用了哈希算法的特性,直接根据组合中的所有元素生成一个唯一不会重复的Key,放入Set中即可

进一步探讨:

  • 若组合情况太多,可将上面的Key分成两部分,构成[Key1 : [key2]]双散列(key2所在容器为Set),以避免单个散列表过大,查找耗费时间过长的问题
  • 若给定数组中的元素并非简单的Int类型,可将元素依据位置转换成Int数组(新数组中的元素代表原数组中的对象的位置),对于重复元素,Int值取该元素第一次出现的位置。 将得到的Int数组放入递归函数进行重排,递归函数返回的排列组合为原数组中元素的位置索引,根据位置索引提取原数组中的元素进行打印即可

3. Swift代码

  • 递归重排函数
func arrangeArray(array: [Int]) -> [[Int]]{
    //元素个数为0时返回空数组
    if array.count == 0 {
        return [[Int]]()
    }
    
    //初始化
    var finalArray: [[Int]] = [[Int]]()
    var hasRepeatNumber = false                 //是否需要去重
    for i in 0 ..< array.count {
        for j in i+1 ..< array.count {
            if array[i] == array[j] {
                hasRepeatNumber = true
                break
            }
        }
        if hasRepeatNumber {
            break
        }
    }
    
//    var hashTable = [String : Set<String>]()    //利用字典和Set实现双散列查找
    var hashSet = Set<String>()                 //利用Set实现单散列查找

    
    //排列组合
    var tempArray = array
    let oneElement = tempArray.removeFirst()                //取出一个元素
    let returnedArray = arrangeArray(array: tempArray)     //剩余元素排列组合

    //将取出的元素插入到递归返回的数组中的每一个数组中的每一个位置,并添加到递归函数要返回的数组中
    if returnedArray.count == 0 {
        finalArray.append([oneElement])
    } else {
        for oneArray in returnedArray {
            for i in 0 ... oneArray.count {
                var newArray = oneArray
                newArray.insert(oneElement, at: i)

//                //双散列去重后添加到finalArray中
//                if hasRepeatNumber {
//                    addNewArrayFilterByDict(hashTable: &hashTable, newArray: &newArray, finalArray: &finalArray)
//                } else {
//                    finalArray.append(newArray)
//                }

                //单散列去重后添加到finalArray中
                if hasRepeatNumber {
                    addNewArrayFilterBySet(hashSet: &hashSet, newArray: &newArray, finalArray: &finalArray)
                } else {
                    finalArray.append(newArray)
                }

            }
        }
    }

    return finalArray
}

private func addNewArrayFilterByDict(hashTable: inout [String : Set<String>], newArray: inout [Int], finalArray: inout [[Int]] ) -> Void {
    var hashKey1 = String()
    var hashKey2 = String()
    for j in 0 ..< newArray.count {
        if j % 2 == 0 {
            if newArray[j] > 9 {                                //此判断对性能无明显影响
                hashKey1 += " " + String(newArray[j]) + " "     //两个相邻的非个位数相邻时多添加了一个空格,若增加一个末尾是否是空格的判断,在非个位数占比较少时明显增加运算时间
            } else {
                hashKey1 += String(newArray[j])
            }
        } else {
            if newArray[j] > 9 {
                hashKey2 += " " + String(newArray[j]) + " "
            } else {
                hashKey2 += String(newArray[j])
            }
        }
    }
    
    if let set = hashTable[hashKey1] {
        //已经有内部哈希表进行查找
        if set.contains(hashKey2) {
            //重复,丢弃
            
        } else {
            finalArray.append(newArray)
            hashTable[hashKey1]?.insert(hashKey2)
        }
    } else {
        //无内部哈希表时添加内部哈希表,注意字典为值类型
        finalArray.append(newArray)
        hashTable[hashKey1] = Set<String>()
        hashTable[hashKey1]?.insert(hashKey2)
    }

}

private func addNewArrayFilterBySet(hashSet: inout Set<String>, newArray: inout [Int], finalArray: inout [[Int]] ) -> Void {
    var hashKey = String()
    for element in newArray {
        if element > 9 {                                    //此判断对性能无明显影响
            hashKey += " " + String(element) + " "          //两个相邻的非个位数相邻时多添加了一个空格,若增加一个末尾是否是空格的判断,在非个位数占比较少时明显增加运算时间
        } else {
            hashKey += String(element)
        }
    }
    
    if hashSet.contains(hashKey) {
        //重复,丢弃
        
    } else {
        finalArray.append(newArray)
        hashSet.insert(hashKey)
    }
}

  • 调用递归函数并打印结果
let array1 = [1, 2, 1, 2, 10, 2, 3, 2, 3, 10, 1, 1, 2, 1]       //双散列查找去重需68s,单散列查找去重需74s
let array2 = [1, 2, 1, 2, 1, 2, 3, 2, 3, 4, 1, 1, 2, 1]     //双散列查找去重需21s,单散列查找去重需27s

let array3 = [1, 2, 3, 4, 5, 6, 4, 2, 3, 1, 2]
let array4 = [1, 2, 1, 2]

//print(Date())
let arrangedArray = arrangeArray(array: array4)
//print(Date())

//print(arrangedArray.count)

//遍历数组并打印
for i in 0 ..< arrangedArray.count {
    //合成新的字符串
    let array = arrangedArray[i]

    var string = ""
    for j in 0 ..< array.count {
        if j == array.count - 1 {
            string += String(array[j])
        } else {
            string += String(array[j]) + " "
        }
    }
    //打印字符串
    print(string)
}
  • 对于复杂类型数组可将元素的位置数组传入递归函数进行排列,然后再根据返回的位置索引进行打印
let stringArray = ["没", "Bug", "没有", "Bug"]
var indexArray = [Int]()
for i in 0 ..< stringArray.count {
    let string = stringArray[i]
    let j = stringArray.index(of: string)!
    indexArray.append(j)
}

//print(indexArray)

let arrangedIndex = arrangeArray(array: indexArray)

//print(arrangedIndex.count)

for oneArray in arrangedIndex {
    var string = ""
    for i in 0 ..< oneArray.count {
        if i == oneArray.count {
            string += stringArray[oneArray[i]]
        } else {
            string += stringArray[oneArray[i]] + " "
        }
    }
    print(string)
}

欢迎留言讨论。

GitHub代码:https://github.com/luqz/ArrangeArrayDemo
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 一、基础知识:1、JVM、JRE和JDK的区别:JVM(Java Virtual Machine):java虚拟机...
    杀小贼阅读 2,362评论 0 4
  • 他走了,头也不回。 我捂着胸口静静望着他远去的背影,他怎么就走了,不是说再怎么吵架都不会离开的吗? 为什么会有窒息...
    云歇阅读 358评论 8 18
  • 前段时间,网盘关停调整潮一波接一波,排名前十的网盘中,已经有6个关停或调整了相关服务,关闭的原因大都有“为配合国家...
    haoning7788阅读 478评论 3 4