【牛客剑指offer】数据结构——链表

从尾到头打印链表

代码与逻辑

        vector<int> demo;
        while(head!=NULL){
           demo.push_back(head->val);
           head=head->next;
        }
        vector<int> res;
        for(int i=demo.size()-1; i>=0; i--){
            res.push_back(demo[i]);
        }
        return res;

1.遍历链表顺序存储到数组demo;
2.将数组demo逆置存储到结果数组;

get

vector的使用:
定义数组时无需考虑长度,动态分配;
push_back():在数组末尾插入元素;
size():返回vector中元素的个数;
reverse():用于反转在[first,last)范围内的顺序(包括first指向的元素,不包括last指向的元素),reverse函数没有返回值。使用需要包含头文件

#include <algorithm>
/**
*  struct ListNode {
*        int val;
*        struct ListNode *next;
*        ListNode(int x) :
*              val(x), next(NULL) {
*        }
*  };
*/
#include <cstddef>5
#include <vector>
class Solution {
public:
    vector<int> printListFromTailToHead(ListNode* head) {
        vector<int> res; //result
        stack<int> s;   //stack 实现逆序 栈先进后出
        //从链头开始顺序入栈
        while(head!=NULL){
            s.push(head->val);  //入栈
            head=head->next;    //头指针后移实现遍历
        }
        //栈先进后出,从栈顶输出,实现逆序输出。栈底向栈顶是正序
        while(!s.empty()){
            res.push_back(s.top()); //栈顶元素加入res 使用pushback实现变长数组增加元素
            s.pop();  //弹出栈顶元素
        }
        return res;
    }
};

反转链表

代码

    ListNode* ReverseList(ListNode* pHead) {
		ListNode *res = NULL; 
		ListNode *cur = pHead;
		while(cur!=NULL){
			//暂存待处理的下一个节点,cur为当前处理中的节点
			ListNode *temp = cur->next;		
			//逆置,原本res在cur之前,将next指向res实现逆置
			cur->next=res;
			//res指向已经处理过的、逆序的一串节点的头节点(最后被处理完的那个节点
			res=cur;
			//处理下一个节点,暂存在temp中
			cur=temp;
		}
		return res;
    }
};

get

cur指向当前节点
temp暂存cur之后要处理的下一个节点
res为结果
在这里插入图片描述

合并两个排序的列表

代码

class Solution {
  public:
    ListNode* Merge(ListNode* pHead1, ListNode* pHead2) {
        //一个已经为空了,直接返回另一个
        if (pHead1 == NULL)
            return pHead2;
        if (pHead2 == NULL)
            return pHead1;
        //加一个表头
        ListNode* head = new ListNode(0);
        ListNode* cur = head;
        //两个链表都要不为空
        while (pHead1 && pHead2) {
            //取较小值的节点
            if (pHead1->val <= pHead2->val) {
                cur->next = pHead1;
                //只移动取值的指针
                pHead1 = pHead1->next;
            } else {
                cur->next = pHead2;
                //只移动取值的指针
                pHead2 = pHead2->next;
            }
            //指针后移
            cur = cur->next;
        }
        //哪个链表还有剩,直接连在后面
        if (pHead1)
            cur->next = pHead1;
        else
            cur->next = pHead2;
        //返回值去掉表头
        return head->next;
    }
};

get

使用双指针思想,较小的节点接入结果链表,指针后移
结果链表:
//加一个表头
ListNode* head = new ListNode(0);
ListNode* cur = head;
随着处理,cur不断后移,接入处理完的、结果链表中的节点
head为该结果链表的表头,为空节点
返回结果时:
//返回值去掉表头
return head->next;

两个链表的第一个公共节点

代码

/*
struct ListNode {
	int val;
	struct ListNode *next;
	ListNode(int x) :
			val(x), next(NULL) {
	}
};*/
class Solution {
public:
    ListNode* FindFirstCommonNode( ListNode* pHead1, ListNode* pHead2) {
		if(pHead1==NULL || pHead2==NULL){
			return NULL;
		}
        //无环——一旦相同,此后完全相同
		//get length
		int len1,len2;
		len1=len2=0;
		ListNode* a=pHead1;
		while(a) {
			len1++;
			a=a->next;
		}
		ListNode* b=pHead2;
		while(b) {
			len2++;
			b=b->next;
		}
		//the longer one's head goes first until equal
		a=pHead1;
		b=pHead2;
		if(len1>len2){	//p1 more
			for(int i=0; i<len1-len2;i++){
				a=a->next;
			}
		}
		else if(len1<len2){
			for(int i=0; i<len2-len1; i++){
				b=b->next;
			}
		}
		//two lists equal, once same val, stop
		while((a->val != b->val) && (a!=NULL) && (b!=NULL)){
			a=a->next;
			b=b->next;
		}
		return a;
	}
};

get

思路:
双指针

  1. 遍历两个链表得到各自的长度
  2. 双指针回到链表头,较长的指针后移,直到双指针至表尾的长度相同
  3. “无环”,则在公共的一串元素之前,所有元素均不相同。一旦遇到相同元素,则之后一定是相同元素串。
  4. 遍历等长的链表串,双指针同步后移,在均未到表尾的前提下,遇到相同元素即停止。
  5. 到表尾也停止,返回双指针指向的null

注意:
判断条件加()
while((a->val != b->val) && (a!=NULL) && (b!=NULL))
开始时先判断是否有空的列表,有则直接返回null

链表中倒数第k个元素

代码

    ListNode* FindKthToTail(ListNode* pHead, int k) {
        // write code here
        ListNode* a=pHead;
        int len=0;
        while(a){
            len++;
            a=a->next;
        }
        if(len<k){
            return nullptr;;
        }
        int ddl = len-k;
        ListNode* res = pHead;
        for(int i=0; i<ddl; i++){
            res = res->next;
        }
        return res;
    }
};

get

两次遍历

  1. 得到表长
  2. 通过表长得到倒数第k个为正向第 length-k+1个,遍历得到

注意:
正向第 length-k+1个
遍历时
int ddl = len-k;
for(int i=0; i<ddl; i++)
或者
int ddl = len-k+1;
for(int i=0; i<=ddl; i++){

删除元素中重复的点

代码

    ListNode* deleteDuplication(ListNode* pHead) {
        
        int MaxSize = 1001;
        int count[MaxSize];
        ListNode* a = pHead;
        //遍历链表,节点值作为数组的下标,对应数组元素+1
        if(a==NULL){
            return NULL;
        }
        while (a != NULL) {
            count[a->val]++;
            a = a->next;
        }
        //遍历数组,元素值为1的纳入结果
        pHead->next = NULL;
        a = pHead;
        for (int i = 0; i < MaxSize; i++) {
            if (count[i] != 1) {
                continue;
            } else {
                ListNode* temp = new ListNode(i);
                a->next = temp;
                a = a->next;
            }
        }
        return pHead->next;
    }

get

以空间换时间
值有范围,新建数组,容量为所有值的数量。
遍历链表,节点值作为数组的下标,对应数组元素+1。
遍历数组,只考虑元素值为1的数组元素,标明元素有且仅有一个。其余则为未出现或重复出现。
元素值为1的数组元素,下标作为链表节点的值,尾插法归入结果链表中。

删除链表中的元素

代码

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 *	ListNode(int x) : val(x), next(nullptr) {}
 * };
 */
    ListNode* deleteNode(ListNode* head, int val) {
        // write code here
        if(head==nullptr){
            return nullptr;
        }
        ListNode* res=head;
        ListNode* a=head;
        ListNode* aPre=a;
        if(a->val==val){
            head=head->next;
            return head;
        }
        while(a!=nullptr){
            if(a->val!=val){
                aPre=a;
                a=a->next;
            }
            else{
                a=a->next;
                aPre->next=a;
            }
        }
        return res;
    }

get

要删除的元素在链表中时,保存pre进行删除。
要删除的节点在表头时,头指针后移——需要单独处理这种情况

链表中环的入口节点

代码


/*
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        ListNode* fast=pHead;
        ListNode* slow=pHead;
        while(fast!=nullptr){
            slow=slow->next;
            if(fast->next==nullptr){
                return nullptr;
            }
            fast=fast->next->next;
            if(fast==slow){
                fast=pHead;
                while(fast!=slow){
                    fast=fast->next;
                    slow=slow->next;
                }
                return fast;
            }
        }
        return nullptr;
    }

get

思路:
快慢指针
当链表中有环时,使用快慢指针(快的一次走两个,慢的一次走一个),两指针终将相遇。
若可以相遇,表示有环。

算法逻辑:
假设从头节点到环的入口节点的前一个节点一共有a个,环中的节点有b个。
设fast指针走过的节点数是f,slow指针走过的节点数是s。
那么有以下两个结论:
f = 2 * s (即快指针走过的节点数一定是慢指针的两倍)
f = s + nb (当两者相遇时,快指针一定已经绕环走了n圈)
由上面两个等式可以得出,f = 2nb,s = nb
故可知,两指针相遇时,慢指针已经走了nb步。
已知我们要走到入口节点,需要走a + kb步,而这时s = nb只要再走a即可到达入口。
我们把快指针移动到头节点,然后两个指针一步一步往后走,当它们相遇时所处的位置就是入口节点
——相遇时两者在入口节点,则快指针走了a,同步的慢指针走了a。故两者同步后移,相遇时即为入口节点。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值