【数据结构与算法】递归和逆置

147 阅读2分钟

线性数据结构的遍历

let arr = [1,2,3,4]

// 数组的基本遍历
function traverse(arr) {
    if (arr == null) return
    for (let i = 0; i < arr.length; i++) {
        console.log(arr[i])
    }
}
traverse(arr)

function Node(value) {
    this.value = value
    this.next = null
}

let node1 = new Node(1)
let node2 = new Node(2)
let node3 = new Node(3)
let node4 = new Node(4)
let node5 = new Node(5)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5

// 链表遍历
function traverseLink(root) {
    let temp = root
    while (true) {
        if (temp != null){  // 严格判断下 undefined!==null 所以要使用 !=
            console.log(temp.value)
        } else {
            break
        }
        temp = temp.next
    }
}
traverseLink(node1)

image.png

变形:递归遍历

let arr = [1, 2, 3, 4, 5, 6]

// function traverseArr(arr) {
//     if (arr == null) return
//     for (let i = 0; i < arr.length; i++) {
//         console.log(arr[i])
//     }
// }

// 数组递归遍历
function traverseArr(arr, i) {
    if (arr == null || arr.length <= i) return
    console.log(arr[i])
    traverseArr(arr, i + 1)
}

traverseArr(arr, 0)

function Node(value) {
    this.value = value
    this.next = null
}

let node1 = new Node(1)
let node2 = new Node(2)
let node3 = new Node(3)
let node4 = new Node(4)
let node5 = new Node(5)

node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5

//链表递归遍历 注:必须有出口
function traverseLink(root) {
    if (root == null) return
    console.log(root.value)
    traverseLink(root.next)
}

// traverseLink(node1)

链表逆置

image.png

  1. 总体思路:用 4 和 5 来举例子,本来是 4 指向 5, 现在需要让 5 指向 4, 然后 4 指向空;进而比较 3 和 4, 2 和 3, 1 和 2,结束标志是由最开始的 5 指向空变为 1 指向空。

  2. 操作链表时,要注意链表不能断。

  3. 递归需要满足:

    1. 递归元素操作相同
    2. 存在出口
  4. 指向改变的顺序问题:以 4 和 5 为例,如果4 先指向 null,那么需要5 指向 4 的时候,将找不到 5, 因为 5 是根据 4找到的(指向的)。所以正确的顺序是先从5指向4,再从 4指向 null,保证链表不能断。

  5. 判断出口问题:let result = reverseList(root.next) 调用了函数,所以执行了 1,2,3,4的指向改变,最后由5指向了4,而4在之前已经已经指向了3,也就不存在4指向null的问题,result缓存的是最后的结果,也就是出口,所以最终返回的result就是出口5(newRoot)。

function Node(value) {
    this.value = value
    this.next = null
}

let node1 = new Node(1)
let node2 = new Node(2)
let node3 = new Node(3)
let node4 = new Node(4)
let node5 = new Node(5)

node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5

// 先找递归出口,在往下递归
function reverseList(root) {
    if (root == null || root.next == null) return root
    // 暂存下一步操作,避免污染
    // 表面上,从root开始操作;实际上,从newRoot开始操作
    // 只不过操作到newRoot的时候,其他元素已经默默地改变指向了
    let result = reverseList(root.next)
    root.next.next = root
    root.next = null
    return result
}

let newRoot = reverseList(node1)

function traverseLink(root) {
    if (root == null) return
    console.log(root.value)
    traverseLink(root.next)
}

traverseLink(newRoot)