首页 > 知识百科 > 正文

C++数据结构与算法——链表原创

C++第二阶段——数据结构和算法,之前学过一点点数据结构,当时是基于Python来学习的,现在基于C++查漏补缺,尤其是树的部分。这一部分计划一个月,主要利用代码随想录来学习,刷题使用力扣网站,不定时更新,欢迎关注!

文章目录

一、删除链表元素(力扣203)二、设计链表(力扣707)三、翻转链表(力扣206)四、两两交换链表中的节点(力扣24)五、删除链表的倒数第N个结点(力扣19)六、链表相交(力扣面试题02.07链表相交)七、环形链表Ⅱ(力扣142)

一、删除链表元素(力扣203) )

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足的 Node.val == val 的节点,并返回新的头节点

头结点单独处理

/** * 单链表的定义。 * 结构ListNode { * int val; * 列表节点 *下一个; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x),下一个(下一个){} * }; */class 解决方案 { public: ListNode* removeElements(ListNode* head, int val) { //先判断头结点是不是val,如果是的话,head就后移 while(head!=NULL  && head->val==val){ ListNode * temp ; 临时 = head;=  head->下一个; 删除 temp ; } //定义一个指针指向中间过程的结点 ListNode * cur = head; while(cur!= NULL&&cur->下一个!=NULL){ if(cur->下一个->val== val){ / / 删除下一个结点 ListNode *temp = cur ->下一个; cur->下一个= cur->下一个->下一个; 删除 temp; } else{ cur = cur->next; } } 返回 head; } };

虚拟头节点

/** * 单向链表的定义. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */class 解决方案 {public: ListNode* removeElements(ListNode* head, int val) { //定义一个虚拟头结点 ListNode * dummyHead =  ListNode(<跨度CLass="token number">0); dummyHead ->next = head; // 一个定义路径结点 ListNode *cur = dummyHead; while(cur!=NULL&&cur->下一个! =NULL){  if(cur->下一个->val==val){ ListNode * temp = cur->下一个; cur>->下一个 = cur->下一个-> 下一个; 删除 temp; } else{ cur = cur->下一个; } } < span class="3967-301e-1db2-3edb token punctuation">返回 dummyHead->next; }};

二、设计链表(力扣707)

你可以选择使用单链表或者双链表,设计并实现自己的链表。
单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。
是个体链表,则如果还需要属性 prev 来指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。
实现 MyLinkedList 类:
MyLinkedList() 初始化 MyLinkedList 对象。
int get(int index) 获取链表中下标为索引的节点的值。如果下标无效,则返回 -1 。
void addAtHead(int val) 将一个值 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表
void addAtTail(int val) 将值 val 的节点追加到链表中作为链表的最后一个元素。
void addAtIndex(intindex, int val) 将一个值 val 的节点插入到链表中下标为索引的节点之前。如果索引等于链表的长度,那么该节点会被追加到链表的补​​充。如果索引比长度更大,该节点
void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为索引的节点。
示例:
输入
[“MyLinkedList”, “addAtHead”, “addAtTail”, “addAtIndex”, “get”, “deleteAtIndex”, “get”]
[[], [1], [3], [1, 2] , [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]
解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2); // 链表变为 1->2->3
myLinkedList.get(1); // 返回 2
myLinkedList.deleteAtIndex(1); // 现在,链表变为 1->3
myLinkedList.get(1); // 返回 3
提示:
0 <= index, val <= 1000
请不要使用内置的 LinkedList 库。
调用 get、addAtHead、addAtTail、addAtIndex 和 deleteAtIndex 的次数不超过2000。

//创建一个结点class LinkNode{< /span>public: LinkNode(int inputVal){ 这个->val =< /span> inputVal; } int val; LinkNode *下一个= NULL;};class MyLinkedList { dummyHead=  LinkNode(0); 大小=0; //获取链表长度 } int 获取(int索引) { if(索引<0||索引>(这个->大小-1)){ 返回 -1; } LinkNode * cur = dummyHead->下一个 ; while(index--< span class="9697-e961-6b78-3967 token operator">){ cur = cur ->下一个; } 返回 cur->val; }< /span> void addAtHead(int val) { LinkNode * insertNode =  LinkNode(val ); insertNode->下一个= dummyHead->下一个; dummyHead->下一个=insertNode; 大小++; } void addAtTail( int val) {  // 找到尾部 LinkNode * cur = dummyHead;< /span> while(cur!=NULL&&cur->下一个!=NULL){ if(索引>=->大小+1 ){ showLink< span class="e961-6b78-3967-301e token function">();  return; } else if(index == ->大小){ addAtTail(val); 返回; span> } else if(索引<=0){ addAtHead(val); return; } LinkNode * cur = dummyHead; while(索引--){ cur = cur->下一个;  } //添加 LinkNode * insertNode =  LinkNode(val); insertNode->下一个= cur->下一个; cur->下一个 = insertNode; 大小++;< /span> } void deleteAtIndex(int索引) { 如果(索引<0||索引> (这个- >大小-1)){ 返回 ; } LinkNode * cur  = dummyHead; while(索引--){ cur = cur->下一个 ; } //删除 LinkNode * temp = cur->下一个; cur->下一个 = cur->下一个->下一个; 删除 temp;< /span> temp = NULL; 大小--; } LinkNode * dummyHead= new  LinkNode(0); int 大小=< /span>0; //获取链表长度 < span class="e32e-d2b9-ca43-9f6d token comment">void showLink( ){ LinkNode * cur= dummyHead; while(cur!=NULL&&cur->下一个!=NULL){ cout<<cur< span class="9f6d-5c4a-7235-3898 令牌运算符">->下一个->val<<" "; cur = cur ->下一个; } cout<<endl; }};// 6,7,2,0,4, // 4/** * 您的 MyLinkedList 对象将被实例化并按如下方式调用: * MyLinkedList* obj = new MyLinkedList(); * int param_1 = obj->get(索引); * obj->addAtHead(val); * obj->addAtTail(val); * obj->addAtIndex(index,val); * obj->deleteAtIndex(索引); */

三、翻转链表(力扣206)

给你单链表的头节点 head ,请你链食物表,并返回工具后的链表。

双剪刀,注意先移动pre再移动cur。

< code class="9f6d-5c4a-7235-3898 token comment">/** * 单链表的定义。 * 结构ListNode { * int val; * 列表节点 *下一个; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * } ; */class 解决方案 {public: ListNode* reverseList(ListNode* head) { if(head==NULL||head->下一个==NULL){ //只有一个结点 < span class="3967-301e-1db2-3edb token comment">返回 head; } //定义两个指针,一个指前面的,一指后面的 ListNode *pre = NULL; ListNode *cur = head; while(cur!=NULL){ //用一个值去接收curListNode *temp = cur->下一个; cur->下一个 = pre;= cur; cur = temp; } 返回 pre; }};


另外一种通过电位下降的方式碳水化合物链表,等增益电位再补上

四、两两交换链表中的节点 (力扣24)

给你一个链表,两两交换其中后继的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

每两个交换一次,需要找到交换之前的节点。注意交换实的逻辑,对于不变量需要提前保存信息。

/** * 单链表的定义。 * 结构ListNode { * int val; * 列表节点 *下一个; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x),下一个(下一个){} * }; */class 解决方案 {public: ListNode* 交换对(ListNode* head) { if(head==NULL|| ->下一个==NULL ){ 返回 head ; } //创建一个虚拟头结点 ListNode * dummyNode =  ListNode(0)< span class="ca43-9f6d-5c4a-7235 token punctuation">; dummyNode->下一个 =head; ListNode * cur =  dummyNode; while(cur->下一个!=NULL&&当前->下一个->下一个! =NULL){  ListNode * a = cur-> next; ListNode * b= cur->下一个->下一个->下一个; cur->下一个= cur->下一个->下一个;  cur->下一个->下一个= a; a->下一个 = b; cur = cur->下一个->下一个; } return dummyNode->next;  }}; 

五、删除链表的倒数第N个结点(力扣19)

给你一个链表,删除链表的倒数第一个n个结点,并返回链表的头结点。

双指针的经典问题,快指针先走n个,快慢指针再同时走,当快指针下一个为NULL时,慢指针下一个即为要删除的元素

 /** * 单链表的定义. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x ), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */class< /span> 解决方案 {public: ListNode* removeNthFromEnd(ListNode* head, int n) { if (head==NULL){ 返回 head; } ListNode * dummyNode =  ListNode(0); dummyNode->下一个 = head; /使用快慢指针 ListNode* 快速 = dummyNode; ListNode*< /span> 慢= dummyNode; while(n--) { //快指针先走n个快速 = 快速->下一个; } 同时(快速!= NULL&&快速->下一个< span class="9f6d-5c4a-7235-3898 token 操作符">!=NULL){ 快速 = 快速->下一个;=->下一个 ; } //删除元素 ListNode * temp = Slow>->下一个;->下一个=< /span> 慢->下一个->下一个; 删除 temp; //删除 return dummyNode->next; } };

六、链表相交(力扣面试题02.07链表相交)

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的开始节点。如果两个链表没有交点,返回 null 。

/** * 单链表的定义。 * 结构ListNode { * int val; * 列表节点 *下一个; * ListNode(int x) : val(x), next(NULL) {} * }; */class 解决方案 {public: ListNode * getIntersectionNode(ListNode *headA, ListNode *headB) { //说明是找到合适的结点,不是值正确,而是同一个结点 //将两个链表右对齐//求两个链表的长度 if(headA ==NULL||headB==NULL){ 返回 NULL; } int lenA =1; int lenB =1; ListNode * curA = headA; ListNode *< /span> curB = headB; while(curA->下一个!=NULL){ lenA++; curA = curA->< /span>next; } while(curB->下一个!=NULL){ lenB++; curB = curB->下一个; }< /span> //寻找最短的链表 int minLen =min(lenA,lenB); // AB需要移动的长度 int needA= lenA-minLen; int needB = lenB - minLen; //右对齐两个节点 while(needA--){ headA = headA->下一个; }  while(needB--)< span class="3967-301e-1db2-3edb token punctuation">{ headB = headB->下一个; } // 比较之后的链表是否相同 while(headA!=NULL||headB!=NULL){ if(headA== headB){ 返回 headA; } else{ headA = headA->下一个; headB = headB - >下一个; } } 返回 NULL; } };

七、环形链表Ⅱ(力)扣142)

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪下一个指针再次到达,则链表中环。为了表示给定链表中的环,运动系统内部使用整数 pos 来表示链表尾部连接到链表中的位置(索引从 0 开始) 。如果 pos 是-1,则修改在该链表中没有环。注意:pos不作为参数进行传递,为了达到标识链表的实际情况。
不允许链表。

/** * 单链表的定义。 * 结构ListNode { * int val; * 列表节点 *下一个; * ListNode(int x) : val(x), next(NULL) {} * }; */class 解决方案 {public: ListNode * detectCycle(ListNode *head) { //使用一个地图记录访问过的地址,不断将地址放入map,如果某次map的长度没有发生变化,说明入环了 map<ListNode *,ListNode *> addmap; if(head ==NULL){ 返回 NULL; } //创建虚拟头结点 ListNode  * dummyHead =  ListNode(0); dummyHead->下一个 =< /span> head; //遍历 ListNode * cur = dummyHead; while(cur!=NULL){ //记录之前的大小  intfirstSize=addmap.大小< span class="e32e-d2b9-ca43-9f6d tokenfunction">(); addmap.插入(make_pair(cur->下一个,cur->下一个)); int endSize = addmap 大小(); if(endSize< span class="ff7b-9697-e961-6b78 token punctuation">==firstSize){ //没有发生变化,说明已经入环 返回 addmap[cur->下一个]; } 其他< span class="ca43-9f6d-5c4a-7235 token keywords">{ cur = cur->下一个; } } 返回 NULL; } };


使用map存储每个结点的next,如果map存完之后长度没有变,那么说明进入环了,map中存储的值即为要返回的索引值。
使用双指针

/** * 单链表的定义。 * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class 解决方案 { ListNode * 第一个 =; ListNode *= head;  while(first!=NULL&&第一个->下一个!=NULL){ 第一个 = 第一个->下一个-> 下一个;=->下一个; if(==){ //此时相遇 ListNode * index1 = 第一个; ListNode * index2 = head; //找入环口 while(index1!=index2) {index1=index1 ->下一个;index2=index2->下一个;  } 返回 index1; } } 返回 NULL< /span>; }};

C++数据结构与算法——链表原创由知识百科栏目发布,感谢您对的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“C++数据结构与算法——链表原创