秋招备战-算法系列(链表题)

2019年的秋招已经陆陆续续的开始了,在这里记录下自己准备秋招过程中的一些算法题,题目来源主要是剑指offer以及leetcode:

基础题:

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

思路:
s1:从头到尾遍历链表,并将每一个节点的值存入栈中
s2:将栈中的元素出栈

/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> res;
        stack<int> array;
        ListNode* node = head;
        int temp;
        while(node != NULL){
            array.push(node->val);
            node = node->next;
        }
        while(!array.empty()){
            temp = array.top();
            res.push_back(temp);
            array.pop();
        }
        return res;
    }
};

进阶题:

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

思路:
暴力解法:
s1: 从头到尾遍历链表,统计链表中节点的个数为n
s2: 找到倒数第k个节点,倒数第k个节点为从头节点开始的第n-k+1个节点
优化解法(双指针)
s1:先将第一个指针p1向后走到第k个节点
s2:再将第二个指针p2放到第一个节点处
s3:将这两个节点同时向后遍历,当p1到达链表的尾部时,此时p2的位置为倒数第k个节点

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
        if(pListHead == NULL){
            return pListHead;
        }
        ListNode* p1 = pListHead;
        ListNode* p2 = pListHead;
        for(int i = 0; i < k-1; i++){
            if(p1->next != NULL){
                p1 = p1->next;
            }else{
                return p1->next;
            }
        }
        while(p1->next != NULL){
            p1 = p1->next;
            p2 = p2->next;
        }
        return p2;
    }
};

输入一个链表,反转链表后,输出新链表的表头。

这题主要考察指针翻转
思路:
在这里插入图片描述

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* ReverseList(ListNode* pHead) {
        if(pHead == NULL){
            return NULL;
        }
        ListNode* pNode = pHead;
        ListNode* pre = NULL;
        ListNode* pReverseHead = NULL;
        while(pNode != NULL){
            ListNode* pNext = pNode->next;
            if(pNode->next == NULL){
               pReverseHead = pNode;
            }
             pNode->next = pre;
             pre = pNode;
             pNode = pNext;
        }
        return pReverseHead;
    }
};

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

这一题主要采取递归思路
s1:从头节点分别遍历两个链表
s2:将两个节点中最小的值赋给当前节点,并继续比较含有小节点链表的下一个节点和另外一个链表的当前节点。
s3:重复s2直到两个链表的当前节点都为空

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
    {
        //当一个链表遍历结束,指向另外一个链表
        if(pHead1 == NULL){
            return pHead2;
        }
        else if(pHead2 == NULL){
            return pHead1;
        }
        ListNode* curNode = NULL;
        if(pHead1->val < pHead2->val){
            curNode = pHead1;
            curNode->next = Merge(pHead1->next, pHead2);
        }else{
            curNode = pHead2;
            curNode->next = Merge(pHead1, pHead2->next);
        }
        return curNode;
    }
};

输入两个链表,找出它们的第一个公共结点。

思路:
从定义来看,该题为两个单链表,因此节点重合后没有分叉,如下:
在这里插入图片描述
由于两个链表长度有可能不一致,在开始遍历两个链表之前先得出链表的长度,如图长链表比短链表长一个元素,先将开始遍历一个长链表节点,之后再同时遍历,解题思路如下:
s1:得出两个链表长度
s2:将长链表先遍历超出部分,当剩下的节点数与短链表节点数相同时,同时开始遍历
s3:当两个链表的节点相等时停止遍历

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    int getLength(ListNode* pHead){
        ListNode* pNode = pHead;
        int length = 0;
        while(pNode != NULL){
            pNode = pNode->next;
            ++length;
        }
        return length;
    }
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
        int length1 = getLength(pHead1);
        int length2 = getLength(pHead2);
        
        ListNode* pNode1 = pHead1;
        ListNode* pNode2 = pHead2;
        
        if(length1 >= length2){
            int dif = length1-length2;
            for(int i = 0; i < dif; i++){
                pNode1 = pNode1->next;
            }
        }
        if(length1 < length2){
            int dif = length2-length1;
            for(int i = 0; i < dif; i++){
                pNode2 = pNode2->next;
            }
        }
        while((pNode1 != NULL) && (pNode2 != NULL) && (pNode1 != pNode2)){
            pNode1 = pNode1->next;
            pNode2 = pNode2->next;
        }
        
        return pNode1;
    }
};

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

思路:
在头节点前加一个dummy节点,定义两个指针pPre(重复节点前的节点)初始值指向头节点之前的节点,pNode(当前遍历节点)初始值为头节点。
在这里插入图片描述

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* pHead)
    {
        if(!pHead || !pHead->next) return pHead;
        ListNode* dummy=new ListNode(-1);
        dummy->next=pHead;
        ListNode *pPre=dummy;
        ListNode *pNode=pHead;
        bool flag=0;
        while(pNode && pNode->next){
            while(pNode->next && pNode->val==pNode->next->val){
                pNode->next=pNode->next->next;
                flag=1;
            }
            if(flag==1){
                pPre->next=pNode->next;
                flag=0;
                pNode=pNode->next;
            }
            else{
                pNode=pNode->next;
                pPre=pPre->next;
            }
        }
        return dummy->next;
    }
};

拔高题

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

关于这一题,可以拆分为三个步骤:
s1:复制原始链表的每一个节点并且用next连接起来
s2:复制每一个节点的random指针
s3:拆分原始链表和复制好的链表

/*
struct RandomListNode {
    int label;
    struct RandomListNode *next, *random;
    RandomListNode(int x) :
            label(x), next(NULL), random(NULL) {
    }
};
*/
class Solution {
public:
    void NodeClone(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode != NULL){
            RandomListNode* pCloned = new RandomListNode(pNode->label);
            
            pCloned->label = pNode->label;
            pCloned->next = pNode->next;
            pCloned->random = NULL; 
            
            pNode->next = pCloned;
            pNode=pCloned->next;
        }
    }
    
    void NodeRandom(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        while(pNode != NULL){
            RandomListNode* pCloned = pNode->next;
            if(pNode->random != NULL){
                pCloned->random = pNode->random->next;
            }
            pNode = pCloned->next;
        }
    }
    
    RandomListNode* NodeDisConnnect(RandomListNode* pHead){
        RandomListNode* pNode = pHead;
        RandomListNode* pClonedHead = NULL;
        RandomListNode* pCloned = NULL;
        
        if(pNode != NULL){
            pClonedHead = pCloned = pNode -> next;
            pNode->next = pCloned->next;
            pNode = pNode->next;
        }
        while(pNode != NULL){
            pCloned->next = pNode->next;
            pCloned = pCloned->next;
            pNode->next = pCloned->next;;
            pNode = pNode->next;
        }
        return pClonedHead;
    }
    
    RandomListNode* Clone(RandomListNode* pHead)
    {
        if(pHead == NULL){
            return pHead;
        }
        NodeClone(pHead);
        NodeRandom(pHead);
        return NodeDisConnnect(pHead);
    }
};

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

这一题受前面倒数第k个数的题的启发,设置两个指针来完成
思路如下:
s1:寻找到环中的任意一个节点
定义两个指针,一个一次走两步,一个一次走一步,如果链表内存在环,两个指针就会在环内的某一个节点相遇
s2:求出环中节点的个数
在环内定义两个指针,一个一次走1步,一个固定不动,当两个指针相遇时走的步数+1为节点数
s3:找出环的入口
定义两个指针,一个指向头节点,一个移动n(环内节点个数)步,两个节点相遇的位置为环入口处

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* getMeetingNode(ListNode* pHead)
    {
        if(pHead == NULL)
            return pHead;

        ListNode* slowNode = pHead->next;
        if(slowNode == NULL)
        {
            return slowNode;
        }
        ListNode* quickNode = slowNode->next;
        while(quickNode != NULL && slowNode != NULL)
        {
            if(quickNode == slowNode)
            {
                return quickNode;
            }
            slowNode = slowNode->next;
            quickNode = quickNode->next;
            
            if(quickNode != NULL)
                quickNode = quickNode->next;
        }
        return nullptr;
    }
    
    ListNode* EntryNodeOfLoop(ListNode* pHead)
    {
        ListNode* meetNode = getMeetingNode(pHead);
        if(meetNode == NULL)
            return meetNode;
        
        ListNode* node1 = meetNode;
        int length = 1;
        while(node1->next != meetNode)
        {
            node1 = node1->next;
            ++length;
        }
        node1 = pHead;
        for(int i = 0; i < length; ++i)
        {
            node1 = node1->next;
        }
        ListNode* node2 = pHead;
        while(node2 != node1)
        {
            node2 = node2->next;
            node1 = node1->next;
        }
        return node2;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值