单链表基本操作

article/2025/10/7 7:53:38

 目录

结构体,这里讨论的都是带头节点的

一、单链表建立

1、头插法:利用头指针控制链表节点的增加

代码:

2、尾插法:

二、遍历

三、插入:(pos代表要插入的位置)

四、删除

五、销毁

六、查找 

单链表的查找分为按位查找和按值查找(只讨论带头结点)

七、带头结点的插入排序

八、删除无序链表中值重复出现的节点(两种解决方法)

 九、反转一个单链表

十、单链表的归并排序

十一、待补充

总结:单链表基本操作集合

代码:myself


结构体,这里讨论的都是带头节点的

typedef struct Node{int value;struct Node* next;
}*Link;

一、单链表建立

1、头插法:利用头指针控制链表节点的增加

核心:
newNode->next = head->next;
head->next = newNode;

 

代码:

//头插法创建链表
Link headCreateLink(int n){//头指针初始化部分Link head,newNode;head = (Link)malloc(sizeof(struct Node));head->next = NULL;while(n--){newNode = (Link)malloc(sizeof(struct Node));scanf("%d",&newNode->value);// 主要核心,新节点指向头指针的下一节点,头指针指向新节点。newNode->next = head->next;head->next = newNode;}return head;
}


2、尾插法

需要借助一个辅助指针rear,指向当前链表最后一个节点,每次处理辅助指针指向的节点和新增的节点的关系即可。

核心:
newNode->next = rear->next;
rear->next = newNode;
rear = rear->next;

 

//尾插法创建链表
Link rearCreateLink(int n){//头指针初始化以及rear指针初始化指向头指针。Link head,rear,newNode;head = (Link)malloc(sizeof(struct Node));head->next = NULL;rear = head;while(n--){newNode = (Link)malloc(sizeof(struct Node));scanf("%d",&newNode->value);// 主要核心,新节点指向rear的下一节点,rear的下一节点指向新节点(顺序切记不可搞反),rear指向新节点。newNode->next = rear->next;rear->next = newNode;rear = rear->next;} return head;
}


二、遍历

void TraverseList( LinkList L )
{LinkList p = L->next ;while( p ){printf( "%d " , p->data ) ;//可以对结点的值进行修改p = p->next ;}printf( "\n" ) ;
}

三、插入:(pos代表要插入的位置)

Status ListInsert( LinkList *L , int pos , int e )
{int j = 1 ;LinkList p , s ;p = *L ;while( p && ( j < pos ) ){p = p->next ;j++ ;}if( !p || (  j > pos ) )return ERROR ;s = ( LinkList )malloc( sizeof( Node ) ) ;s->data = e ;s->next = p->next ;p->next = s ;return OK ;
}

四、删除

Status ListDelete( LinkList *L , int pos , int *val )
{int j = 1 ;LinkList p , q ;p = *L ;while( ( p->next ) && j < pos ){p = p->next ;j++ ;}if( !( p->next ) ||  ( j > pos ) )return ERROR ;q = p->next ; //注意这行不能和下面互换位置p->next = q->next ;*val = q->data ;free( q ) ;//一定要free掉要删除的结点,防止内存溢出return OK ;
}

五、销毁

销毁单链表:
Status DestoryList( LinkList *L )
{LinkList p , q ; //如果只是free(*L)是不够的,因为这样的话只是删除了链表的头节点,而且直接这样删除的话就找不到头节点以后的节点了p = ( *L )->next;while( p ){q = p->next ;free( p ) ;p = q ;}( *L )->next = NULL ;return OK ;
}

六、查找 

单链表的查找分为按位查找和按值查找(只讨论带头结点)

根据值查找:

LinkList *GetByNum(LinkList *head,int num){LinkList *p=head->next;while(p!=NULL){if(p->data==num)return p;//找到返回结点指针 p=p->next;} return NULL;//找不到返回NULL 
}

根据节点查找: 

LinkList *GetById(LinkList *head,int i){int j=0;if(i<=0)return NULL;//保证序号合法 LinkList *p=head;while(p->next!=NULL){p=p->next;j+=1;//以头节点指向的节点为序号1对应的节点 } if(i==j)return p;return NULL;
}
//求表的长度
int Length(LinkList L){int len = 0;LNode *p = L;while(p->next!=NULL){p=p->next;len++;}return len;
}

七、带头结点的插入排序

c语言带头节点的单链表插入排序_ejdjdk的博客-CSDN博客_c语言链表头插法排序

八、删除无序链表中值重复出现的节点(两种解决方法)

删除无序链表中值重复出现的节点(两种解决方法)_Whynotwu的博客-CSDN博客_无序单链表删除重复节点

 九、反转一个单链表

【图文解析】反转一个单链表_giturtle's blog-CSDN博客_反转链表

十、单链表的归并排序

​​​​​​怎样实现链表的归并排序_weixin_30644369的博客-CSDN博客

写给自己看的单链表(5):归并排序_lyrich随便写写-CSDN博客_有头结点的单链表归并排序

写给自己看的单链表(2):进阶操作_lyrich随便写写-CSDN博客

十一、待补充


总结:单链表基本操作集合

删除
(1)删除链表中值最小的节点
(2)删除链表中值最小的节点并由最后一个节点补充
(3)删除第K个位置的节点
(4)删除给定值的节点
(5)删除倒数第K个位置的节点

插入
(1)在第i个位置后面插入

查找
(1)统计单链表中节点值为给定值的节点个数

排序
(1)插入排序
(2)单链表实现选择排序
(3)归并排序
(4)将单链表化为分两个部分,左半部分小于给定值val,右半部分大于等于给定值val

其他
(1)反转链表
(2)删除相同的多余节点的算法
(3)合并两个单链表
(4)将单链表调整为堆,并在堆中插入一个元素,使其还是堆

代码:myself

#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <set>using namespace std;typedef struct link
{int val;struct link *next;
}*Link, LinkNode;void create(Link &head)	//带头结点, 默认头结点的位置编号为0, 尾插法 
{Link newNode, rear;int val, cnt;head = new LinkNode;head->next = NULL;rear = head;cout << "你输入节点个数:";cin >> cnt;for(int i = 0; i < cnt; i ++ ){newNode = new LinkNode;cin >> newNode->val;newNode->next = rear->next;rear->next = newNode;rear = newNode;}
}void show(Link L)
{L = L->next;if(L == NULL){cout << "链表为空" << endl;return ;	} cout << "链表如下:" << endl;while(L){cout << L->val << " ";L = L->next;}cout << endl;
}void InsertSort(Link &head)
{Link pre, r, cur, tmp;cur = head->next->next;head->next->next = NULL;while(cur){pre = head;r = head->next;while(r && r->val < cur->val){pre = pre->next;r = r->next;}tmp = cur;cur = cur->next;pre->next = tmp;tmp->next = r;}
}Link merge(Link L1, Link L2)	//合并两个升序的单链表并使新的单链表仍然有序 
{Link head, p1, p2, rear;	p1 = L1->next;p2 = L2->next;head = new LinkNode;head->next = NULL;rear = head;while(p1 && p2){if(p1->val < p2->val)	{//不要加上了p1->next = rear!!!这里不是插入节点!!! rear->next = p1;rear = p1;p1 = p1->next;}else{rear->next = L2;rear = L2;	p2 = p2->next;}}rear->next = p1 ? p1 : p2;return head;}void deleteMinNode(Link &L, int &min_val)	//直接删除最小值节点 
{Link minPre, cur;int min;minPre = L;cur = L->next;if(cur == NULL){cout << "链表为空!" << endl;return ;}min = cur->val;while(cur->next){if(cur->next->val < min){min = cur->next->val;minPre = cur;}cur = cur->next;}cur = minPre->next;minPre->next = cur->next;free(cur); 		//删除就要释放 min_val = min;
}void swapMinNode(Link &L, int &min_val)	//删除最小值节点并由最后一个节点补充 
{//思路:将最小值节点替换为最后一个节点,然后删除最后一个节点 Link minPre, cur, curPre;int minx;minPre = L;curPre = L;cur = curPre->next;if(cur == NULL)	{cout << "链表为空!" << endl;	return ;}minx = cur->val;while(cur->next){if(cur->next->val < minx){minx = cur->next->val;minPre = cur;}curPre = cur;cur = cur->next;}minPre->next->val = cur->val; curPre->next = cur->next;free(cur);min_val = minx;
}void deleteByPos(Link &L, int pos)	//删除pos位置的节点 
{int j = 1;Link pre, goal;	//第k个节点的前驱节点, 目标节点 pre = L;while(pre->next && j < pos){j ++ ;pre = pre->next;}goal = pre->next;if(pos < j || goal == NULL){cout << "节点不存在!" << endl;return ;}pre->next = goal->next;free(goal);
}void insertNode(Link &L, int pos)	//在pos位置之前插入节点 
{int j = 1, val;Link newNode, preNode;preNode = L;while(j < pos && preNode){	j ++ ;preNode = preNode->next;} if(pos < j || preNode == NULL){cout << "插入位置不正确" << endl;return ;}cout << "请输入要插入的数据: ";cin >> val; newNode = new LinkNode;newNode->val = val;newNode->next = preNode->next; preNode->next = newNode;
}void deleteNodeByNum(Link &L, int val)	//删除具有给定值val的节点 
{Link pre, goalNode;	 pre = L;if(pre->next == NULL){cout << "链表为空!" << endl;return ;}while(pre->next){if(pre->next->val == val){goalNode = pre->next;pre->next = goalNode->next;free(goalNode);}else{/*当有连续的两个val值的时候只会删除一个因为在删除一个val之后pre后移一位指向了下一个val值的节点 所以当我们删除了一个val值节点之后不要后移pre指针 */pre = pre->next;}}
}int getValNum(Link &L, int val)	//查找节点值为给定值val的节点个数 
{L = L->next;int cnt = 0;if(L == NULL){cout << "链表为空" << endl;return 0;}while(L){if(L->val == val)	cnt ++ ;L = L->next;	} return cnt;
}void SelectSort(Link &L)//单链表上的简单选择排序算法
{int tmp;Link p, q, minp;for(p = L->next; p; p = p->next)	//遍历所有节点, 注意跳过头结点 {minp = p;	//默认当前节点是值最小的节点 for(q = p->next; q; q = q->next)	//在当前节点和后面所有节点中找到一个值最小的节点 if(q->val < minp->val)minp = q;if(minp != p)	//如果当前节点不是最小值节点就交换值 {tmp = p->val;p->val = minp->val;minp->val = tmp;}}
}//归并排序 -- 就地归并 
Link MergeListCore(Link head1, Link head2) //两个单链表head1和head2的归并排序 
{//如果head1或者head2==NULL,直接返回另一个 if (head1 == NULL) return head2;if (head2 == NULL)return head1;//如果head1和head2均不为空,让值小的作为头, if (head1->val <= head2->val) {head1->next = MergeListCore(head1->next, head2); //head1作为头 return head1;}if (head1->val > head2->val) {head2->next = MergeListCore(head1, head2->next);  //head2作为头 return head2;}
}Link MergeSortCore(Link head)
{if (head->next == NULL)	//只有一个节点返回 return head;//如果区间长度不为1,递归划分左右区间 //左区间长度为len/2上取整,右区间长度为len/2下取整,即左区间长度减去右区间长度等于1/0 	Link slow, fast;slow = head;fast = slow->next;while (fast != NULL) {fast = fast->next;if (fast != NULL) { slow = slow->next;fast = fast->next;}}Link righthead;righthead = slow->next;slow->next = NULL;
//	cout << "text: " << head->val << " " << righthead->val << endl;//继续划分 head = MergeSortCore(head);righthead = MergeSortCore(righthead);return MergeListCore(head, righthead);
}void MergeSort(Link &head)
{if(head->next == NULL)	return ;head->next = MergeSortCore(head->next);
} bool checkLift(Link &L)	//判断一个单链表是否递增, 这里相等也算递增 
{Link pre = L->next;if(pre == NULL){cout << "链表为空!" << endl;return false;}while(pre->next != NULL){if(pre->next->val < pre->val)	return false;else	pre = pre->next;}return true;
} Link partition(Link head, int x) //根据x的值把单链表分割成两个区间 
{/*pre指针的作用:pre指向可以与cur的值做交换的节点如果cur的值小于x,我们需要把cur的值放到链表的前面但又希望小于x的值在排序后仍然保持原来的顺序,即保持稳定性例如,原本是1,2,我们希望排序后仍然是1,2而不是2,1 那么我们就要记录当前已经遍历到的小于x的最后一个元素的后继,就是pre那么初始时pre就要放在第一个元素的位置了 pre左侧的元素肯定时小于x的 因为只要cur的值小于x,pre就要后移一位而在初始状态下,pre和cur是指向同一元素的只有当当前元素的值大于x时,cur移动,而pre不移动也就说明[pre,cur)这个区间的值肯定时大于x的我们不会把一个小于x的值交换到右侧 cur指针的作用:找到一个小于x的值与pre交换,因为pre肯定在cur的左边所以小于x的就可以被放到左半区 */ Link pre = head->next;	 Link cur = head->next;	//cur遍历单链表所有元素 while(cur){if(cur->val < x){int temp = pre->val;pre->val = cur->val;cur->val = temp;pre = pre->next;}cur = cur->next;}return head;
}//删除链表中的重复元素  
void deleteDuplecate(Link &L)//方法一:哈希表法,时间复杂度O(n) 额外空间复杂度O(n)
{set<int> S;Link cur, pre;pre = L;cur = L->next;if(cur == NULL){cout << "链表为空" << endl;return ;}while(cur != NULL){if(S.count(cur->val))	//不需要修改pre指针,因为此时pre就指向了cur后移的位置 pre->next = cur->next;else{S.insert(cur->val);pre = cur;	}cur = cur->next;}
} Link reverseList(Link &head)	//反转链表 
{//思路:头插入创建一个新链表 Link newHead;Link tmpNode;newHead = new LinkNode;newHead->next = NULL;head = head->next;while(head != NULL){tmpNode = head;head = head->next; tmpNode->next = newHead->next;newHead->next = tmpNode;/*head = head->next;必须放在tmpNode->next = newHead->next;的后面否则tmpNode指向newHead,head也会指向newHead 例如下面的test()! */}return newHead;
}void test(Link L)
{//list:1 2 3 4 5Link p, q;q = L->next;p = q;p->next = NULL;if(q->next == NULL)	cout << "NULL";else	cout << q->next->val;//输出NULL
} void deleteNthFromEnd(Link &head, int n)	//删除倒数第k个节点 
{// 设链表长度为len // 双指针法,让fast先走n步,然后fast和slow一起走,// 当fast走到链表尾部(NULL)处,fast一共走了len步,slow走了len-n步// 正数len-n就是倒数第n的位置 if(head->next == NULL){cout << "链表为空" << endl;return ;}Link slow = head, fast = head, pre;for(int i = 0; i < n; i ++ )	fast = fast->next;while(fast != NULL){fast = fast->next;pre = slow;slow = slow->next;}pre->next = slow->next;
}int main()
{int min;Link L1, L2;create(L1);	show(L1);create(L2); show(L2);Link L3 = merge(L1, L2);show(L3);return 0;
}


http://chatgpt.dhexx.cn/article/PoSOJb21.shtml

相关文章

单链表的基本操作

文章目录 单链表(含头结点)一、单链表二、单链表的创建(有头结点)三、单链表的结点查找(按位置查找)四、单链表的插入操作五、链表的删除操作六、链表的逆置七、链表的置空八、链表的销毁 单链表(含头结点) 一、单链表 1.1 关于单链表 单链表是一种采用链式存储的线性结构&am…

单链表的操作(超详细),保证你看完不后悔

&#x1f30d;新人小白的博客 ⌛️希望大家多多关注 &#x1f331;一起加油&#xff0c;共同成长 &#x1f383;以后会经常更新哒~&#x1f648; ⭐️个人主页&#xff1a; 收藏加关注&#xff0c;永远不迷路~⭐️ 数据结构系列&#x1f440; 一&#xff1a;顺序表的操作&…

C语言单链表实现初始化、创建、增、删、查等基本操作(详细)

C语言单链表实现初始化、创建、增、删、查等基本操作 #include <stdio.h> #include <stdlib.h> #include <malloc.h> typedef int ElemType; //定义单链表结构 typedef struct Node {ElemType data;//数据域struct Node *next;//指针域&#xff0c;指向下一…

java线程池的工作原理_JAVA线程池原理详解一

线程池的优点 1、线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用。 2、可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。 线程池的创建 1 public ThreadPoolExecutor(int corePoolSize, 2 in…

线程池原理解析

1、为什么要用线程池&#xff0c;线程池的作用&#xff08;意义&#xff09;&#xff0c;如果使用线程池会有什么好处&#xff0c;说说你对线程池的了解&#xff1f; 创建和销毁线程的代价是很大的&#xff0c;线程不像创建普通object对象那样&#xff0c;在堆中分配点内存就好…

Java 线程池原理及四种常用的线程池使用

推荐阅读&#xff1a;Java线程池实现原理及其在美团业务中的实践 文章目录 什么是线程池使用线程池的好处线程池的实现原理流程图分析源码分析 线程池的使用向线程池中提交任务newCachedThreadPoolnewFixedThreadPoolnewScheduledThreadPoolnewSingleThreadExecutor自定义线程池…

Tomcat线程池监控及线程池原理分析

目录 一、背景 二、tomcat线程池监控 三、tomcat线程池原理 四、总结 一、背景 我们都知道稳定性、高可用对于一个系统来讲是非常重要的&#xff0c;而为了保证系统的稳定性&#xff0c;我们一般都会进行各方面的监控&#xff0c;以便系统有任何异常情况时&#xff0c;开发人员…

Python学习:线程池原理及实现

传统多线程方案会使用“即时创建&#xff0c; 即时销毁”的策略。尽管与创建进程相比&#xff0c;创建线程的时间已经大大的缩短&#xff0c;但是如果提交给线程的任务是执行时间较短&#xff0c;而且执行次数极其频繁&#xff0c;那么服务器将处于不停的创建线程&#xff0c;销…

超详细的线程池原理解析

说明 线程池作为常用的并发工具重要性不言而喻&#xff0c;本文针对线程池进行了抽丝剥茧般的深入解析&#xff0c;希望大家看后会有帮助。 1 ThreadPoolExecutor结构关系图 2 参数结构 public ThreadPoolExecutor( int corePoolSize,//核心线程数量int maximumPoolSize,…

从简单代码入手,分析线程池原理

一、线程池简介 1、池化思想 在项目工程中&#xff0c;基于池化思想的技术应用很多&#xff0c;例如基于线程池的任务并发执行&#xff0c;中间件服务的连接池配置&#xff0c;通过对共享资源的管理&#xff0c;降低资源的占用消耗&#xff0c;提升效率和服务性能。 池化思想…

线程池原理与实现

目录 1. 线程池是什么 2. 线程池的优点&#xff1a; 3. 线程池的应用场景 4. 线程池的实现 4.1 线程池实现原理 4.2 线程池基本框架 4.3 结构体&#xff1a; 4.4 提供的接口 4.5 线程池测试代码 5 线程池提高demo thrd_pool.h thrd_pool.c main.c 运行结果 6 re…

线程池原理——高频面试题

1.高频面试题&#xff1a; 1.为什么使用线程池&#xff0c;优势是什么&#xff1b; 2.线程池如何使用&#xff1b; 3.线程池的几个重要的参数介绍&#xff1b; 4.线程池底层工作原理&#xff1b; 5.线程池用过吗&#xff1f;生产上你如何设置合理参数&#xff1b; 2.线程…

Android线程池原理详解

简介 但凡有点开发经验的同学都知道&#xff0c;频繁的创建和销毁线程是会给系统带来比较大的性能开销的。所以线程池就营运而生了。那么使用线程池有什么好处呢&#xff1f; 降低资源消耗 可以重复利用已创建的线程降低线程创建和销毁造成的消耗。提高响应速度 当任务到达时…

Java面试题之:线程池原理

Java面试题之&#xff1a;线程池原理 一、简介二、线程复用三、线程池的组成四、拒绝策略五、Java 线程池工作过程 一、简介 线程池做的工作主要是控制运行的线程的数量&#xff0c;处理过程中将任务放入队列&#xff0c;然后在线程创建后启动这些任务&#xff0c;如果线程数量…

ThreadPoolExecutor线程池原理

ThreadPoolExecutor线程池原理 线程池原理1. 线程池的简单介绍1.1 线程池是什么1.2 线程池解决的核心问题是什么 2. 线程池的实现原理2.1 线程池的执行流程2.2 源码分析 3. 线程池的使用3.1 线程池的创建3.2 向线程池提交任务3.3 生命周期管理3.4 关闭线程池3.5 合理地配置线程…

线程池原理分析

使用线程池目的 在开发过程中&#xff0c;合理地使用线程池能够带来3个好处。 1.降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 2.提高响应速度。当任务到达时&#xff0c;任务可以不需要等到线程创建就能立即执行。 3.提高线程的可管理性。线程是稀…

Java 线程池原理总结

Java 线程池原理总结 &#xff08;一&#xff09;什么是线程池 线程池做的工作主要是控制运行的线程的数量&#xff0c;处理过程中将任务放入队列&#xff0c;然后在线程创建后启动这些任务&#xff0c;如果线程数量超过了最大数量超出数量的线程排队等候&#xff0c;等其它线…

线程池原理总结

【引言】 关于线程池&#xff0c;印象中好像自己有写过相关的总结博客。翻了翻之前的博客&#xff0c;确实&#xff0c;在去年十一月写过一篇《线程池使用总结》。 时隔一年&#xff0c;我已经离开了那家让我成长很多的公司&#xff0c;在那里&#xff0c;写了很多的代码&…

线程池原理全解析

目录 1 线程池简介 2 线程池 2.1 ThreadPoolExecutor类 2.2 ThreadPoolExecutor方法 3 线程池实现原理 3.1.线程池状态 3.2.任务的执行 总结过程 3.3.线程池中的线程初始化 3.4.任务缓存队列及排队策略 3.5.任务拒绝策略 3.6.线程池的关闭 3.7.线程池容量的动态调…

一文带你清晰弄明白线程池的原理

不知道你是否还记得阿里巴巴的java代码规范中对多线程有这样一条强制规范: 【强制】线程资源必须通过线程池提供&#xff0c;不允许在程序中显示创建线程。 说明&#xff1a;使用线程池的好处是减少在创建和销毁线程池上所消耗的时间以及系统资源的开销&#xff0c;解决资源不足…