算法面试中单链表专题

链表是算法面试中一个比较常考的内容,下面以面试中常见的算法题作为切入点:


首先来道开胃菜:92. 反转链表
算法面试中单链表专题_第1张图片


解题思路: 这道题反转,我们可以考虑以头插法的方式来解决,这样一个好处是,我们对中间的链表进行反转之后,重新插入只需要考虑尾部,头部不用考虑了,举例来说:上面的 2->3->4,通过尾插法之后,1->4最后连接不用管了,而只需要关心2->5的连接。

代码如下

class Solution {
     
    public ListNode reverseBetween(ListNode head, int m, int n) {
     
    	ListNode dummyhead = new ListNode(0);  // 建立虚拟头节点,下面以例子 1->2->3->4->5 来说
    	dummyhead.next = head;
    	ListNode fakehead = head;    // 反转起始节点
    	ListNode pre = dummyhead;    // 这里是例子中的 节点1的位置,也就是反转起始节点前一个节点
    	int count = 1;
    	while(count < m && fakehead != null){
     
    		count ++;
    		pre = pre.next;
    		fakehead = fakehead.next;
    	} // 找到相应的位置
    	ListNode cur = fakehead;  // 保存反转起始节点,这个节点最终反转结束后要指向 5,防止丢失
    	pre.next = null;
    	while(fakehead != null && count <= n){
       // 开始反转
            count++;
    		ListNode nexts = fakehead.next;
    		fakehead.next = pre.next;
    		pre.next = fakehead;
    		fakehead = nexts;
    	}
    	cur.next = fakehead;  // 结束后,指向5位置
    	return dummyhead.next;
    }
}

同理,这还有道差不多的题目:25. K 个一组翻转链表,上面的代码改改就能提交了:


算法面试中单链表专题_第2张图片

稍微改动一下,或者直接复用代码就行了:

class Solution {
     
    public ListNode reverseKGroup(ListNode head, int k) {
     
        ListNode count = head;
        int c = 0;
        while(count != null) {
     count = count.next; c++;} // 算出节点总数
        int n = 1;
        ListNode temphead = head;
        while(n+k-1 <= c){
     
            temphead = reverseBetween(temphead,n,n+k-1); // 每次反转k个节点,
            n = n+k;
        }
        return temphead;
    }

    public ListNode reverseBetween(ListNode head, int m, int n) {
     
    	ListNode dummyhead = new ListNode(0);  // 建立虚拟头节点,下面以例子 1->2->3->4->5 来说
    	dummyhead.next = head;
    	ListNode fakehead = head;    // 反转起始节点
    	ListNode pre = dummyhead;    // 这里是例子中的 节点1的位置,也就是反转起始节点前一个节点
    	int count = 1;
    	while(count < m && fakehead != null){
     
    		count ++;
    		pre = pre.next;
    		fakehead = fakehead.next;
    	} // 找到相应的位置
    	ListNode cur = fakehead;  // 保存反转起始节点,这个节点最终反转结束后要指向 5,防止丢失
    	pre.next = null;
    	while(fakehead != null && count <= n){
       // 开始反转
            count++;
    		ListNode nexts = fakehead.next;
    		fakehead.next = pre.next;
    		pre.next = fakehead;
    		fakehead = nexts;
    	}
    	cur.next = fakehead;  // 结束后,指向5位置
    	return dummyhead.next;
    }
}

或者像下面这样,同样的思想:

class Solution {
     
    public ListNode reverseKGroup(ListNode head, int k) {
      
        ListNode dummyhead = new ListNode(0);
        dummyhead.next = head;
        ListNode fakehead = head;  // 这几个变量的意义是和上面那道题是一样的
        ListNode pre = dummyhead;
        pre.next = null;
        int c = 0, sum = 0;
        ListNode count = head;
        while(count != null) {
     count = count.next; sum++;}  // 统计节点个数
        int tempsum = 0;
        while(tempsum + k <= sum){
         // 统计节点
            ListNode cur = fakehead;  // 记录反转节点第一个节点
            while(c < k)   //反转
            {
     
                ListNode nexts = fakehead.next;
                fakehead.next = pre.next;
                pre.next = fakehead;
                fakehead = nexts;
                c++;
            }
            c = 0;  
            cur.next = fakehead;  // 同上一题
            pre = cur;   // 将头节点移动到上一反转链表的尾节点
            tempsum += k; // 记录已经反转的节点数
        }
        return dummyhead.next;
    }
}

下面这种类型是把排序和单链表结合,面试中极有可能碰到:

147. 对链表进行插入排序

算法面试中单链表专题_第3张图片
直接上代码:

class Solution {
     
    public ListNode insertionSortList(ListNode head) {
     
        if(head == null) return head;
        ListNode dummyhead = new ListNode(0); // 建立虚拟头结点
        dummyhead.next = head;
        ListNode cur = head.next; // 这里是未排序好的第一个节点,也就是待排序的节点
        head.next = null; // 这个地方注意一下,这里是把排序好的和未排序好的分割开,否则容易形成环
        ListNode fakehead = dummyhead; // 游标从虚拟头结点开始扫描
        while(cur != null){
     
            int val = cur.val;
            ListNode nexts = cur.next;
            while(fakehead.next != null && fakehead.next.val < val){
       // 找到正确插入位置
                fakehead = fakehead.next;
            }
            cur.next = fakehead.next;  // 找到正确位置后插入
            fakehead.next = cur;
            fakehead = dummyhead;  // 复位
            cur = nexts;
        }
        return dummyhead.next;
    }
}

这里引入快慢指针的考点,看下例题:
算法面试中单链表专题_第4张图片
这个题,没用快慢指针的话,我的思路是这样的:

class Solution {
     
    public ListNode rotateRight(ListNode head, int k) {
     
        ListNode fakehead = head;
        if(fakehead == null) return head;
        int count = 1; // 链表长度
        while(fakehead.next != null){
     
            count++;
            fakehead = fakehead.next;
        }
        k = count - (k % count); // 首节点需要移动的距离
        fakehead.next = head;  // 首尾连接起来
        while(k > 0){
       // 首节点开始移动
            k--;
            fakehead = fakehead.next;
        }
        head = fakehead.next; // 新的首结点
        fakehead.next = null; // 把环切断
        return head;
    }
}

引入快慢指针后 o(╥﹏╥)o,感觉代码还长些~~~:

class Solution {
     
    public ListNode rotateRight(ListNode head, int k) {
     
        if(head == null)return head;
        int len = 0;
        ListNode findlen = head;
        while(findlen != null){
     
            findlen = findlen.next;
            len++;  // 链表长度
        }
        k = k%len;
        ListNode fast = new ListNode(-1);
        fast.next = head;
        ListNode slow = fast;
        ListNode fakehead = head;
        int i = 0;
        while(head != null){
      // fast快指针移动k个位置之后,慢指针开始移动
            if(i >= k)
                slow = slow.next;
            fast = fast.next;
            i++;
            head = head.next;
        }
        fast.next = fakehead; // 首尾连接,暂时形成环
        ListNode newhead = slow.next;
        slow.next = null; //  切断环
        return newhead;
    }
}

你可能感兴趣的:(大厂面试算法指南,算法以及数据结构,链表,面试,链表反转)