新冠肺炎肆虐,不如在家练练基础算法 | 链表篇

新冠肺炎肆虐,不如在家练练基础算法 | 链表篇

今年春节,新型冠状肺炎以迅雷不及掩耳盗铃儿响叮当之势迅速席卷了全国各地。我所在的大学正处于疫情事件的中心武汉,所以更加深入的感觉到这一场突如其来的国家公共卫生事件的严重性。面试

从几天前武汉市全面封城开始,全国从上到下都在推行不出门、不聚会的号召。算法

今年春节或许成为近几年来最为“冷清”的春节。在这个节骨眼上,也终于到了啥也不干躺在家里就能给社会作贡献的时候。也正是由于疫情的严重性,国家和各大公司相继推迟春节假期和工做开学的时间。数组

对我的而言,或许在为社会作贡献的同时,也能够趁机提高本身,从而能够为迎接下一次面试而作好准备。数据结构

在互联网招聘的面试环节中,手撕算法环节每每会与数据结构的考察相结合。各类经典的算法都离不开经常使用数据结构的支持。ide

本文将从最为基础的数据结构-链表出发,从leetcode中选择几道表明性的题目进行讲解,从而让同窗们可以对链表的基本操做和变形算法有比较全面的了解和认识。oop

01
认识『链表』指针

链表的结构十分简单,其自己是一种线性的存储结构,经过物理地址不连续的节点相链接成链。最简单的单链表只包含一条链,而且每个节点包括两部份内容,数据元素和下一个节点的地址。所以可经过已知节点访问它的下一个节点。
新冠肺炎肆虐,不如在家练练基础算法 | 链表篇code

相比于数组而言,因为链表没必要须按顺序存储,于是在插入的时候能够达到O(1)的复杂度,通常而言比数组的效率要高得多;可是查找一个节点或者访问特定编号的节点则须要O(n)的时间,而数组因为能够直接经过下标寻址,相应的时间复杂度仅为O(1)。blog

通常可经过定义结构体的方式来实现链表:排序

// Definition for singly-linked list.
 struct ListNode {
      int val;
      ListNode *next;
      ListNode(int x) : val(x), next(NULL) {}
 };

02
高频算法题

  1. 输入一个链表,输出该链表中倒数第k个结点。

快慢指针的方法在链表相关的操做中常用。

经过设置快慢指针,快指针先前进k步,以后快慢指针同时前进,此时快慢指针间隔k步;当快指针到达链表尾部,此时慢指针所在节点即为倒数第k个节点。这样的方法仅经过一次遍历便可得到倒数第k个节点。
代码以下:

class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if(pListHead==NULL||k==0)
            return NULL;
        ListNode*pTail=pListHead,*pHead=pListHead;
        for(int i=1;i<k;++i)
        {
            if(pHead->next!=NULL)
                pHead=pHead->next;
            else
                return NULL;
        }
        while(pHead->next!=NULL)
        {
            pHead=pHead->next;
            pTail=pTail->next;
        }
        return pTail;
    }
};
  1. 输入一个链表,反转链表后,输出新链表的表头。

非递归方法的解题思路须要理解如下四步,具体见下图:

新冠肺炎肆虐,不如在家练练基础算法 | 链表篇
代码以下:
//第一种方法:非递归方法 /
class Solution {
public:
ListNode
ReverseList(ListNode* pHead) {
if(pHead==NULL)
return NULL;

ListNode* cur = pHead;
    ListNode* pre = NULL;
    ListNode* head = NULL;

    while(cur != NULL)
    {
        if(cur->next==NULL)
            head = cur;
        ListNode* nxt = cur->next;
        cur->next = pre;
        pre = cur;
        cur = nxt;

    }
    return head;
}

};

//第二种方法是:递归方法 /
class Solution {
public:
ListNode
ReverseList(ListNode* pHead) {
//若是链表为空或者链表中只有一个元素
if(pHead==NULL||pHead->next==NULL) return pHead;

//先反转后面的链表,走到链表的末端结点
    ListNode* pReverseNode=ReverseList(pHead->next);

    //再将当前节点设置为后面节点的后续节点
    pHead->next->next=pHead;
    pHead->next=NULL;
    return pReverseNode;

}
};

  1. 将两个有序链表合并为一个新的有序链表并返回。

这题可经过设置双指针的方式对链表不一样节点元素进行筛选:
新冠肺炎肆虐,不如在家练练基础算法 | 链表篇
代码以下:

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        if (l1.val <= l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}
  1. 寻找两个相交链表的公共起点。

这题一样能够设置快慢指针,比较常规的方法能够先求得2个链表的长度,而后让长链表指针先前进两个链表的长度差,而后再一块儿前进;最后当快慢指针相遇的位置便是公共起点。

代码以下:

class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode *pHead1, ListNode *pHead2) {
        int len1 = findListLenth(pHead1);
        int len2 = findListLenth(pHead2);
        if(len1 > len2){
            pHead1 = walkStep(pHead1,len1 - len2);
        }else{
            pHead2 = walkStep(pHead2,len2 - len1);
        }
        while(pHead1 != NULL){
            if(pHead1 == pHead2) return pHead1;
            pHead1 = pHead1->next;
            pHead2 = pHead2->next;
        }
        return NULL;
    }

    int findListLenth(ListNode *pHead1){
        if(pHead1 == NULL) return 0;
        int sum = 1;
        while(pHead1 = pHead1->next) sum++;
        return sum;
    }

    ListNode* walkStep(ListNode *pHead1, int step){
        while(step--){
            pHead1 = pHead1->next;
        }
        return pHead1;
    }
};
  1. 给一个链表,若其中包含环,请找出该链表的环的入口结点,不然,输出null。
    新冠肺炎肆虐,不如在家练练基础算法 | 链表篇

这题一样可采用快慢指针的方法来作。
快指针每次前进2步,慢指针每次前进1步。若链表有环,则快慢指针会在环内相遇。假设图中b是环的入口,a为链表起点,c为快慢指针相遇的位置。因为快慢指针的前进时间相同,则根据速度关系可得:
2(ab+bc)=ab+bc+cb+bc,则得ab=bc
新冠肺炎肆虐,不如在家练练基础算法 | 链表篇

因此当快慢指针相遇后,从相遇点到环入口的距离与从链表头到环入口的距离同样。经过设置一指针从链表头部前进,一指针从相遇点同时前进,两指针相遇的位置便是链表环的入口。
代码以下:

class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode*fast=pHead,*low=pHead;
        while(fast&&fast->next){
            fast=fast->next->next;
            low=low->next;
            if(fast==low)
                break;
        }
        if(!fast||!fast->next)
            return NULL;
        low=pHead;//low从链表头出发
        while(fast!=low){//fast从相遇点出发
            fast=fast->next;
            low=low->next;
        }
        return low;
    }
};

03
总结

链表相关的算法在面试中常常考察,其考察的重点在于对链表结构熟练的操做,包括指针的切换、节点的增删和变形等等。

上面所举的几个例子都是对链表比较基本的处理,能够从中理解到链表经常使用的快慢指针、遍历以及边界判断等问题。

另外,面试中出现的链表相关的高频算法题目可参考如下Leetcode题号:

Leetcode 19 删除链表的倒数第N个节点
Leetcode 21 合并两个有序链表
Leetcode 23 合并K个排序链表
Leetcode 25 K个一组翻转链表
Leetcode 86 分隔链表
Leetcode 109 有序链表转换二叉树
Leetcode 138 复制带随机指针的链表
Leetcode 141 环形链表
Leetcode 160 相交链表
Leetcode 206 反转链表

扫个关注不迷路,校招面试定不误。
新冠肺炎肆虐,不如在家练练基础算法 | 链表篇

推荐阅读(点击下方连接便可阅读)

苦逼研究生的幸运求职路
建议简历写很差的同窗进来瞧一瞧~
非科班如何经过业余时间自学游戏开发,最终收获腾讯网易offer
面试高频算法详解-LRU
生物专业女生教你准备两个月签约AI独角兽
想成为BAT后台开发工程师,这些是基础!

Amazing10承蒙厚爱。

相关文章
相关标签/搜索