LeetCode之链表

  • 首先推荐一个GitHub项目:labuladong。本文是我在做labuladong算法小抄中链表题目的相关源码。
  • 处理链表问题通常可以有迭代法和递归法两种。迭代法思路比较简单,但是写法通常比较复杂;而递归法的思路相对较难,但是写法通常比较简单。在直接做LeetCode之前,先实现几个简单问题的迭代法和递归法。

问题1:翻转整条单链表

方法一:迭代法

public ListNode reverse(ListNode head) {//翻转整条单链表
    ListNode pre, cur, nxt;
    pre = null; cur = head; nxt = head;
    while (cur != null) {
        nxt = cur.next;
        // 逐个结点反转
        cur.next = pre;
        // 更新指针位置
        pre = cur;
        cur = nxt;
    }
    // 返回反转后的头结点
    return pre;
}

方法二:递归法

public ListNode reverse(ListNode head) {//翻转整条单链表
    if (head.next == null) return head;
    ListNode last = reverse(head.next);
    head.next.next = head;
    head.next = null;
    return last;
}

问题2:翻转单链表中的前n个结点

方法一:迭代法

public ListNode reverseN(ListNode head,int n){//翻转链表中的前n个节点
    ListNode last = head;//last存储第n+1个节点
    for (int i = 0; i < n; i++) {
        if(last == null) return head;//不足n个节点就不需要翻转
        last = last.next;
    }
    ListNode pre = last,cur = head,nxt;//pre先初始化为last
    for (int i = 0; i < n; i++) {
        nxt = cur.next;
        //逐个节点翻转
        cur.next = pre;
        //更新节点
        pre = cur;
        cur = nxt;
    }
    return pre;
}

方法二:递归法

ListNode successor = null;//后驱节点
public ListNode reverseN(ListNode head,int n){//翻转单链表中的前n个节点
  if(n == 1){
    successor = head.next;//successor记录第n+1个节点
    return head;
  }
  ListNode last = reverseN(head.next,n-1);//翻转以head.next为起点的前n-1个节点
  head.next.next = head;
  head.next = successor;//让翻转之后的head节点与后面的节点相连接
  return last;
}

92. 翻转链表 II

问题描述:翻转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

方法一:迭代法

public ListNode reverseBetween(ListNode head, int m, int n) {//翻转单链表中的区间[m,n]中的节点
    ListNode cur = head,pre_cur = null,last = head;
    for (int i = 0; i < m-1; i++) {
        pre_cur = cur;//pre_cur是cur的前一个节点。如果cur=head,则pre_cur为null
        cur = cur.next;//cur是第m个节点
    }
    for (int i = 0; i < n; i++) {
        last = last.next;//last存储第n+1个节点
    }
    ListNode pre = last,nxt;//pre初始化为last
    for (int i = 0; i < n-m+1; i++) {//翻转n-m+1次
        nxt = cur.next;
        //逐个节点翻转
        cur.next = pre;
        //更新节点
        pre = cur;
        cur = nxt;
    }
    if(pre_cur ==null){//即m=1,即从第一个节点开始翻转
        return pre;
    }
    else{//翻转后的节点与前半部分链连接
        pre_cur.next = pre;
    }
    return head;
}

方法二:递归法:

public ListNode reverseBetween(ListNode head, int m, int n){//翻转单链表中的区间[m,n]中的节点
  if(m==1){//此时,问题转化为翻转单链表中的前n个节点
    return reverseN(head,n);
  }
  head.next = reverseBetween(head.next,m-1,n-1);//递归head.next
  return head;
}

25. K个一组翻转链表

public ListNode reverseN(ListNode head,int n){//递归法:翻转链表中的前n个节点
  ListNode last = head;//last存储第n+1个节点
  for (int i = 0; i < n; i++) {
    last = last.next;
  }
  ListNode pre = last,cur = head,nxt;//pre先初始化为last
  for (int i = 0; i < n; i++) {
    nxt = cur.next;
    //逐个节点翻转
    cur.next = pre;
    //更新节点
    pre = cur;
    cur = nxt;
  }
  return pre;
}

public ListNode reverseKGroup(ListNode head, int k) {
    if(head==null){
        return null;
    }
    //对于以head为头的链表,则tail指向第k-1个结点
    ListNode tail = head;
    //进行递归的头结点
    ListNode newHead = head;
    //如果不足k个结点,直接返回
    for(int i=0;i<k;i++){
        tail = newHead;
        if(tail==null){
            return head;
        }
        newHead = newHead.next;
    }
    //递归
    tail.next = reverseKGroup(newHead,k);
    //翻转前面k个结点
    return reverseN(head,k);
}

234. 回文链表

方法一:快慢指针找中点再翻转
缺点:这个方法会破坏链表原有的结构

//翻转整条单链表
private ListNode reverse(ListNode head){
    if(head==null || head.next==null){
        return head;
    }
    ListNode last = reverse(head.next);
    head.next.next = head;
    head.next = null;
    return last;
}

public boolean isPalindrome(ListNode head) {
    if(head==null || head.next==null){
        return true;
    }
    //快慢指针找到链表的中点
    ListNode fast = head,slow = head;
    while(fast!=null && fast.next!=null){
        fast = fast.next.next;
        slow = slow.next;
    }
    //翻转后半部分链表
    //如果链表的个数是单数
    if(fast!=null){
        slow = slow.next;
    }
    slow = reverse(slow);
    //判断是否是回文
    while(slow!=null){
        if(head.val!=slow.val){
            return false;
        }
        head = head.next;
        slow = slow.next;
    }
    return true;
}

方法二:链表的后序遍历

//链表也可以有前序遍历和后序遍历,模版如下
public void travel(ListNode head){
	//前序遍历位置
	travel(head.next);
	//后序遍历位置
}
//通过链表的后序遍历
//时间复杂度和空间复杂度都是O(n)
ListNode left = null;
public boolean isPalindrome(ListNode head) {
    left = head;
    return reverse(head);
}
private boolean reverse(ListNode right){
    if(right==null){
        return true;
    }
    boolean res = reverse(right.next);
    //后序遍历
    if(left.val==right.val && res==true){
        left = left.next;
        return true;
    }
    return false;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值