Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

leetcode206:反转链表 #14

Open
sisterAn opened this issue Apr 10, 2020 · 6 comments
Open

leetcode206:反转链表 #14

sisterAn opened this issue Apr 10, 2020 · 6 comments
Labels

Comments

@sisterAn
Copy link
Owner

sisterAn commented Apr 10, 2020

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

附leetcode地址:leetcode

@hh-cheng
Copy link

方法一:遍历

  • 先遍历到尾部
  • 在遍历过程中让节点新增属性 pre 使单向链表变成双向链表
  • 遍历到尾部时开始指向反转并删除节点的 pre 属性

边界条件:

  • 在头部时,节点的 pre 属性为 null
  • 到尾部时,节点的 next 属性为 null
function reverseList(head) {
    // 链表只有一个元素时
    if( head.next === null ) return head;
    // 否则
    let current = head.next,
        preNode = head;
    // 链表反向后原来的头就变成尾了
    preNode.pre = null; // 相当于 head.pre = null;
    while( current.next ){
        current.pre = preNode;  // 建立反向联系
        // 指针后移
        preNode = current;
        current = current.next;
    }
    current.pre = preNode;  // 建立最后一个元素和倒数第二个元素的联系

    // 至此,一个双向链表已经完成了
    // 然后就要开始往前遍历了
    while( current.pre ){
        // 指向反向
        current.next = current.pre;
        // 使"先指针"和"后指针"都指向当前对象
        preNode = current;
        // "先指针"前移
        current = current.pre;
        // 将初始从前向后遍历时新增的属性删掉
        delete preNode.pre;
    }
    // 修正原链表第一个元素的指向
    current.next = current.pre;
    delete current.pre;
}

方法二:递归

  • 每次只对一个元素进行操作,如果当前元素的后一项不为空,则递归调用本函数,传入的值为当前元素的后一项
  • 在每次递归形成的作用域中使后面的元素指向当前元素,当前元素指向空

边界条件:

  • 当传入参数的属性 next 为 null时,说明这个元素是链表的最后一个元素
function reverseList(node) {  // 传入链表的头结点
    if( node.next === null ) {
        /*
        * 能走到这里的有只有两种情况:
        *   1.链表只有一个元素
        *   2.链表不止一个元素同时已经遍历到链表的最后一个元素了
        * */
        return node;
    } else{
        const nextNode = reverseList(node.next);
        nextNode.next = node;
        node.next = null;   // 如果不是原链表的第一个元素,则会在上一次递归的作用域中被修改
        return node;
    }
}

测试代码:

function CreateNode(val){
    this.val = val;
    this.next = null;
}

function CreateList(...nodes){
    this.head = nodes[0];
    this.length = nodes.length;
    for( var i = 0; i < nodes.length - 1; i++ ){
        if( nodes[i+1] ){
            nodes[i].next = nodes[i+1];
        }
    }
}

function reverseList(head){
    // 选择上面所示的任意一个翻转链表的函数
}

const node1 = new CreateNode(1);
const node2 = new CreateNode(2);
const node3 = new CreateNode(3);
const node4 = new CreateNode(4);
const node5 = new CreateNode(5);
const list = new CreateList(node1, node2, node3, node4, node5);
console.log(node1);
reverseList(node1);
console.log(node5);

@sisterAn
Copy link
Owner Author

解法一:迭代法

解题思路: 将单链表中的每个节点的后继指针指向它的前驱节点即可

画图实现: 画图帮助理解一下

确定边界条件: 当链表为 null 或链表中仅有一个节点时,不需要反转

代码实现:

var reverseList = function(head) {
    if(!head || !head.next) return head
    var prev = null, curr = head
    while(curr) {
        // 用于临时存储 curr 后继节点
        var next = curr.next
        // 反转 curr 的后继指针
        curr.next = prev
        // 变更prev、curr 
        // 待反转节点指向下一个节点 
        prev = curr
        curr = next
    }
    head = prev
    return head
};

时间复杂度:O(n)

空间复杂度:O(1)

解法二:尾递归法

解题思路: 从头节点开始,递归反转它的每一个节点,直到 null ,思路和解法一类似

代码实现:

var reverseList = function(head) {
    if(!head || !head.next) return head
    head = reverse(null, head)
    return head
};

var reverse = function(prev, curr) {
    if(!curr) return prev
    var next = curr.next
    curr.next = prev
    return reverse(curr, next)
};

时间复杂度:O(n)

空间复杂度:O(n)

解法三:递归法

解题思路: 不断递归反转当前节点 head 的后继节点 next

画图实现: 画图帮助理解一下

代码实现:

var reverseList = function(head) {
    if(!head || !head.next) return head
    var next = head.next
    // 递归反转
    var reverseHead = reverseList(next)
    // 变更指针
    next.next = head
    head.next = null
    return reverseHead
};

时间复杂度:O(n)

空间复杂度:O(n)

leetcode

@NLRX-WJC
Copy link

思路

反转链表,即是如下操作:
1
准备三个指针:pLeftpMidpRight,分别先指向链表的前三个节点,如下
2
接着,我们只需让pLeftnext指向null,pMidnext指向pLeft,pRightnext指向pMid,并且依次将三个指针同时向前移动,边移动边调整三个指针的next指向,直到pRightnull时,此时pMid为链表的最后一个结点,然后让pMidnext指向pLeft,如下:
3
pMid也就是反转后链表的头结点,最终将pMid返回即可。

代码

/*function ListNode(x){
    this.val = x;
    this.next = null;
}*/
function ReverseList(pHead) {
  if (!pHead) {
    return null
  }
  // 如果传入的链表只有一个元素,那就将原链表返回
  if (!pHead.next) {
    return pHead
  }
  // 准备三个指针
  let pLeft = pHead;
  let pMid = pHead.next;
  let pRight = pMid.next;

  pLeft.next = null
  while (pRight) {
    pMid.next = pLeft;
    // 依次向前移动三个指针
    pLeft = pMid
    pMid = pRight
    pRight = pRight.next
  }
  pMid.next = pLeft
  return pMid
}

@Been101
Copy link

Been101 commented Jul 15, 2020

1 递归甩锅

function reverse_digui(head) {    
  if(!head)  return null     
  if(!head.next)  return head     
      
  const new_head = reverse_digui(head.next) // 先让 head.next 进行反转    
  head.next.next = head  // 反转完成后, head.next 是新链表的尾节点, 再连接上 head    
  head.next = null  // head 成为新链表的尾节点  
  return new_head  // 返回新链表的首节点
}

2 迭代法 // 当前节点指向上一个节点

const reverse_link3 = (head) => {
  let pre_node = null
  let curr_node = head
  while (curr_node) {
    const next_node = curr_node.next // 保存下个节点

    curr_node.next = pre_node // 当前节点指向上一个节点

    // 指针向后滑动
    pre_node = curr_node
    curr_node = next_node
  }

  return pre_node
}

@AAAngelina
Copy link

头插法
var reverseList = function (head) {
if (!head) return head;
let p = head.next, L = head, temp;
L.next = null;
while (p) {
temp = p;
p = p.next;
temp.next = L;
L = temp;
}
return L;
};

@RobinZhao00
Copy link

// nb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

6 participants