c++ 怎样连接两个链表_面试算法题之链表上

正文:5711字 预计阅读时间:15min

    今天给大家带来一些面试常见的链表相关的算法题,具体每一步的含义笔者尽量写清楚一些,因为算法题的步骤很难单纯的靠文字去描述,希望大家可以按照思路在草稿上画一下,自己把思路理一遍,真正理解每一步的含义了,再用编程的语言来写出来。

什么是链表?

链表是一种常见的数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。也就是说链表不需要按顺序来存储数据,在插入的时候可以达到 O(1) 的复杂度。但是在查找的时候需要一个结点一个结点的遍历,所以查找的时间复杂度为O(n)。

1ccb6912f49d8ff90545fd2565512daf.png

链表中的结点包含两个组成一部:结点中的值和指向下一个结点的指针。由此可以构造出链表结点的数据结构:
public class ListNode{       int val;   //值       ListNode next; // 下一个结点的指针       public ListNode(int x){  // 有参构造           this.val = x;       }  }

黑铁:链表翻转问题

题1:翻转单链表(要求掌握递归和迭代的两种方法)

https://leetcode-cn.com/problems/reverse-linked-list/

//迭代解法public ListNode reverseList(ListNode head) {        //1. 数据校验        if(head == null || head.next == null) return head;        //2. 建立一个为null的指针,翻转之后头结点要指向null        ListNode pre = null;        //3. 开始迭代翻转链表        ListNode cur = head;        while(cur != null){            ListNode help = cur.next;            // pre  cur  help            cur.next = pre; //开始翻转            pre = cur; //pre 和 cur 分别往后移动一个结点            cur = help;        }        return pre;    }// 递归解法public ListNode reverseList(ListNode head) {        //1. 数据校验        if(head == null || head.next == null) return head;        //2 . 先递归翻转后面的结点        ListNode cur = reverseList(head.next);        head.next.next = head; //头结点的后面一个结点的指针指向头结点        head.next = null; //头结点的下一个指针指向null        return cur;    }

进阶1 :每两个结点翻转一次单链表

https://leetcode-cn.com/problems/swap-nodes-in-pairs/

public ListNode swapPairs(ListNode head) {        //1. 数据校验        if(head == null || head.next == null) return head;        //2. 定义一个虚拟头结点,使其next指针指向head        ListNode dummy = new ListNode(0);        dummy.next = head;        //3. 开始翻转        ListNode cur = dummy;        //因为要保证后面还有2个结点可以交换,所以后面两个结点不为空        while(cur.next != null && cur.next.next != null){            //定义两个指针保存位置            ListNode a = cur.next;            ListNode b = cur.next.next;            //开始交换,先用 c 来保存后面一个节点的位置            // cur a b             ListNode c = b.next;            cur.next = b;            b.next = a;            a.next = c;            //cur 向后移动            cur = a;        }        return dummy.next;    }

进阶2:每k个结点翻转一次单链表

https://leetcode-cn.com/problems/reverse-nodes-in-k-group/

public ListNode reverseKGroup(ListNode head, int k) {        //1. 数据校验        if(head == null || head.next == null) return head;        ListNode cur = head;        for(int i = 0 ; i < k ; i++){            if(cur == null) return head; //如果没有k个结点了,则直接返回头            cur = cur.next;        }        //2. 此时cur指向第k个结点        ListNode newHead = reverse(head , cur); //翻转前K个结点,返回的是头结点        //递归调用(此时head已经是前k个结点的最后一个,和后面的结点要连接起来)        head.next = reverseKGroup(cur , k);        return newHead;    }    //翻转head  - tail 之间的单链表,返回翻转后的头结点    ListNode reverse(ListNode head , ListNode tail){        ListNode pre = null;        ListNode cur = head;        while(cur != tail){            ListNode help = cur.next;            // pre cur help            cur.next = pre;            pre = cur;            cur = help;        }        return pre;    }

进阶3:每翻转中间m - n个结点的单链表

https://leetcode-cn.com/problems/reverse-linked-list-ii/

public ListNode reverseBetween(ListNode head, int m, int n) {        //1. 建立虚拟头结点        ListNode dummy = new ListNode(-1);        dummy.next = head;        //2. 找到第m-1个结点,也就是开始翻转的结点,用cur保存        ListNode cur = dummy;        for(int i = 1; i < m ; i++) cur = cur.next; //(注意这里是从dummy结点开始的)        //3. a 就是第m个结点        ListNode a = cur.next;        ListNode tail = cur.next; //用tail来保存这个结点,翻转之后是尾结点,注意和之前的链表连接起来        //4. 翻转单链表的操作        ListNode pre = null;        ListNode help = null;        for(int i = m ; i <= n ;i++){            help = a.next;            a.next = pre;            pre = a;            a = help;        }        //5. 翻转完之后pre 是头结点,tail 是尾结点,help是尾结点的下一个结点,        cur.next = pre;        tail.next = help;        return dummy.next;    }

进阶4:验证回文链表

https://leetcode-cn.com/problems/palindrome-linked-list/

思路1:用stack全部压进去再一个个出栈比较(空间复杂度On,不推荐)思路2:翻转后半个单链表,一个个比较(空间复杂度O1,推荐)public boolean isPalindrome(ListNode head) {        //1. 数据校验        if(head == null) return true;        //2. 快慢指针找到链表中点的位置(模板记住!!!)        ListNode fast = head;        ListNode slow = head;        while(fast.next != null && fast.next.next != null){            fast = fast.next.next;            slow = slow.next;        }        //3.翻转后面半个链表        ListNode a = reverse(slow.next);        slow.next = null;//断开结点        //4.开始比较前半个链表和后半个链表,如果出现不同返回false        ListNode b = head;        while(a != null){            if(a.val != b.val) return false;            a = a.next;            b = b.next;        }        return true;    }    // 翻转单链表    ListNode reverse(ListNode head){        ListNode pre = null;        ListNode cur= head;        while(cur != null){            ListNode help = cur.next;            cur.next = pre;            pre = cur;            cur = help;        }        return pre;    }

黄铜:链表相交和环的判断

题1:判断链表中是否有环

https://leetcode-cn.com/problems/linked-list-cycle/

public boolean hasCycle(ListNode head) {        //1. 数据校验        if(head == null || head.next == null) return false;        //2. 定义一个快慢指针,快指针走两步,慢指针走一步,如果链表有环,则肯定相遇        ListNode fast = head;        ListNode slow = head;        while(fast != null && fast.next != null){            fast = fast.next.next;            slow = slow.next;            if(fast == slow) return true;        }        //3. 快指针都到头了,说明链表没有环        return false;    }

进阶1:如果有环找到环结点,没有返回null

https://leetcode-cn.com/problems/linked-list-cycle-ii/

public ListNode detectCycle(ListNode head) {        //1. 数据校验        if(head == null || head.next == null) return null;        //2. 和前一题相似的思路        ListNode fast = head;        ListNode slow = head;        while(fast != null && fast.next != null){            fast = fast.next.next;            slow = slow.next;            if(fast == slow){                //说明有环,下一步就是要找出入环的结点                //将fast指针指向head ,和慢指针每次都直走一步,相遇的就是入环的结点                fast = head;                while(fast != slow){ // 这里肯定会相遇,不用考虑死循环的问题                    fast = fast.next;                    slow = slow.next;                }                return fast;            }        }        return null;    }

题2:找到两个单链表相交的结点

https://leetcode-cn.com/problems/intersection-of-two-linked-lists/

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {        //1. 数据校验        if(headA == null || headB == null) return null;        //2. 定义两个指针分别指向头结点        ListNode l1 = headA , l2 = headB;        //3. l1 一直往后移动,如果到了最后则到另外一个链表的头结点        //  a + c  (链表1)        //  b + c  (链表2)        //  这个思路就是  a + c + b = b + c + a 肯定会到相交的结点        while(l1 != l2){            l1 = l1 == null ? headB : l1.next;            l2 = l2 == null ? headA : l2.next;        }        return l1;    }

白银:合并有序链表问题

题1:合并两个有序链表

https://leetcode-cn.com/problems/merge-two-sorted-lists/

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {        //1. 数据校验        if(l1 == null || l2 == null) return  l1 == null ? l2 : l1;        //2. 建立虚拟头结点        ListNode dummy = new ListNode(-1);        ListNode cur = dummy;        //3. 两个指针分别向后移动,那个结点小就接到cur的后面        while(l1 != null && l2 != null){            if(l1.val < l2.val){                cur.next = new ListNode(l1.val);                cur = cur.next; //cur 也要向后移动一个                l1 = l1.next;            }else{                cur.next = new ListNode(l2.val);                cur = cur.next;                l2 = l2.next;            }        }        //4.剩下的链表直接接上去即可,因为是已经排好序的链表了        cur.next = l1 == null ? l2 : l1;        return dummy.next;    }

题2:合并K个有序链表

https://leetcode-cn.com/problems/merge-k-sorted-lists/

public ListNode mergeKLists(ListNode[] lists) {        //1. 建立小根堆        Queue heap = new PriorityQueue<>((a , b) -> (a.val - b.val));        //2. 将所有头结点放入堆中,这样堆顶就是最小的结点        for(ListNode node : lists){            if(node != null) heap.add(node);        }        //3. 建立虚拟结点        ListNode dummy = new ListNode(-1);        ListNode cur = dummy;        //4. 一直弹出堆顶的结点,放入cur的后面,并将弹出的结点的下一个结点再压入堆中        while(!heap.isEmpty()){            ListNode minNode = heap.poll();            cur.next = minNode;            cur = cur.next;            if(minNode.next != null) heap.add(minNode.next);        }        return dummy.next;    }
一些小建议:
  1. 算法题分类刷,就想笔者这样整理,有利于复习

  2. 模板题要记得很清楚,比如翻转单链表,快慢指针找到中点

  3. 面试时直接写最优解,时间复杂度和空间复杂度能低就低

  4. 面试时可以先和面试官交流思路再开始写代码

  5. 理解最重要,不要背代码,看不懂可以找人问。

  6. 最后,看过的帮我点下拼多多哈哈~6afe523ba862247f22aa43c689347432.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值