C/C++单链表、环形链表、双指针各类算法
C语言
创建链表
插入节点
删除结点
销毁链表(free)
数组模拟单链表
归并排序
快慢指针
len>>1
二进制数右移1,相当于除以2
快指针:一次两个结点
慢指针:一次一个结点
c++
单链表
#include<iostream>
using namespace std;
typedef struct SinglyListNode{
int val;
SinglyListNode *next = NULL;
};
static int size=0;
class MyLinkedList {
public:
int get(int index);//找下标为index的结点,下标为 0-index
void addAtHead(int val);//在头添加结点
void addAtTail(int val);//在尾添加结点
void addAtIndex(int index, int val);//在index前添加结点
void deleteAtIndex(int index);//删除第index结点
void coutLinkedList();//打印链表
private:
//head为地址
SinglyListNode* head=NULL;
};
void MyLinkedList::addAtHead(int val) {
//输入新结点cur
SinglyListNode *cur = new SinglyListNode();
cur->val=val;
cur->next=head;
//指定cur为新head
head=cur;
size++;
}
void MyLinkedList::addAtTail(int val){
if(size==0){
addAtHead(val);
return;
}
SinglyListNode *pLastNode = head;
SinglyListNode LastNode =*pLastNode;
for(int i=0;i<size-1;i++){
pLastNode=pLastNode->next;
}
SinglyListNode *newNode=new SinglyListNode();
newNode->val=val;
newNode->next=NULL;
pLastNode->next=newNode;
size++;
}
void MyLinkedList::addAtIndex(int index, int val){
if(index<=0){
addAtHead(val);
return;
}
if(index==size){
addAtTail(val);
return;
}
if(index>size) return ;
SinglyListNode *SerNode=head;//0 1 2 a 3 4 5 3
//SerNode为index结点的地址,在这个结点前插入一个新的结点
for(int i=1;i<index;i++){
SerNode=SerNode->next;
}
//创建新的结点
SinglyListNode *newNode = new SinglyListNode();
newNode->val=val;
newNode->next=SerNode->next;//新结点的指针指向原链表后一个结点
//SerNode的next指向新结点
SerNode->next=newNode;
size++;
}
void MyLinkedList::deleteAtIndex(int index){
if(index<0||index>=size) return;
SinglyListNode *PrevNode=head;
SinglyListNode *NextNode;
SinglyListNode *MidNode;
if(index==0){
size--;
head=head->next;
delete PrevNode;
return;
}
if(index==size-1){
for(int i=1;i<size;i++){
PrevNode=PrevNode->next;
}
NextNode=PrevNode->next;
PrevNode->next=NULL;
delete NextNode;
size--;
return;
}
for(int i=1;i<index;i++){
PrevNode=PrevNode->next;
}
MidNode=PrevNode->next;
NextNode=MidNode->next;
PrevNode->next=NextNode;
delete MidNode;
size--;
return;
}
int MyLinkedList::get(int index){
if(index>=size||index<0||size==0) return -1;
SinglyListNode *SerNode=head;
for(int i=0;i<index;i++){
SerNode=SerNode->next;
}
cout<<SerNode->val<<endl;
return SerNode->val;
}
void MyLinkedList::coutLinkedList(){
SinglyListNode *a =head;
for(int i=0;i<size;i++){
cout<<a->val<<" ";
if(a->next==NULL) break;
a=a->next;
}
cout<<endl;
}
int main()
{
MyLinkedList linkedList;
linkedList.addAtHead(3);
linkedList.addAtTail(8);
linkedList.addAtIndex(1,5);
linkedList.coutLinkedList();
linkedList.get(0);
linkedList.deleteAtIndex(2);
linkedList.coutLinkedList();
return 0;
}
运行结果:
双指针技巧
环形链表Ⅰ
快慢指针最终会相遇
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL) return false;
ListNode *slow =head;
ListNode *fast =head;
do{
if(slow->next!=NULL) slow=slow->next;
else return false;
if(fast->next!=NULL) fast=fast->next;
else return false;
if(fast->next!=NULL) fast=fast->next;
else return false;
}while(slow!=fast);
return true;
}
};
环形链表Ⅱ
快慢指针摆放的位置以及走过路程的数学关系
(图源力扣,侵删)
- 先利用快慢指针到第一个结点,再把其中一个指针放回开头,每次1步,最终相遇点就是入环节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL) return NULL;
ListNode *fast=head;
ListNode *slow=head;
do{
if(slow->next!=NULL) slow=slow->next;
else return NULL;
if(fast->next!=NULL) fast=fast->next;
else return NULL;
if(fast->next!=NULL) fast=fast->next;
else return NULL;
}while(fast!=slow);
slow=head;
while(fast!=slow){
slow=slow->next;
fast=fast->next;
}
return slow;
}
};
相交链表
算法:变轨
- 一句小评论(特别有意思):
- “走到尽头见不到你,于是走过你来时的路,等到相遇时才发现,你也走过我来时的路。从此我们一路同行,直到生命尽头。”
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==NULL||headB==NULL) return NULL;
ListNode *p1=headA;
ListNode *p2=headB;
int cnt=0;
do{
if(p1==p2) return p1;
if(p1->next!=NULL) p1=p1->next;
else {
p1=headB;
cnt++;
}
if(p2->next!=NULL) p2=p2->next;
else {
p2=headA;
cnt++;
}
}while(cnt<=2);
if(p1==p2) return p1;
else return NULL;
}
};
删除链表的倒数第N个结点
算法:快慢指针
先将fast移动n,然后慢指针也开始移动,当fast正好到结尾,慢指针正好到倒n+1个结点
/**
* Definition for singly-linked list.
* 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 Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head==NULL) return head;
ListNode *fast=head;
ListNode *slow=head;
for(int i=0;i<n;i++){
fast=fast->next;
}
if(fast==NULL) return head->next;//如果快指针为空,则要删除的是头结点,返回下一个结点的地址
while(fast->next!=NULL){
fast=fast->next;
slow=slow->next;
}
slow->next=slow->next->next;
return head;
}
};
总结(双指针模板与注意事项)
模板:
// Initialize slow & fast pointers
ListNode* slow = head;
ListNode* fast = head;
/**
* Change this condition to fit specific problem.
* Attention: remember to avoid null-pointer error
**/
while (slow && fast && fast->next) {
slow = slow->next; // move slow pointer one step each time
fast = fast->next->next; // move fast pointer two steps each time
if (slow == fast) { // change this condition to fit specific problem
return true;
}
}
return false; // change return value to fit specific problem
-
- 在调用 next 字段之前,始终检查节点是否为空。
获取空节点的下一个节点将导致空指针错误。例如,在我们运行 fast = fast.next.next 之前,需要检查 fast 和 fast.next 不为空。
- 在调用 next 字段之前,始终检查节点是否为空。
-
- 仔细定义循环的结束条件。
反转链表
递归解决
/**
* Definition for singly-linked list.
* 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 Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==NULL||head->next==NULL) return head;
ListNode *p2=reverseList(head->next);
head->next->next=head;
head->next=NULL;
return p2;
}
};