c/c++单链表的创建和删除,插入,反转操作

本文详细介绍了C/C++中的链表数据结构,包括链表的创建、插入、删除及反转等基本操作,并提供了实际代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">链表是c/c++中的一个重要的数据结构</span>

首先,他在物理存储上是非连续的,跟数组正好相反,数组是一系列连续的存储单元,这样的特性可能会使链表的访问时间复杂度O(n)高于数组O(1),但是对于链表的插入操作可以达到O(1),删除同理,所以当我们处理数据可能会有大量的插入或者删除操作时,用链表存放数据就非常适用啦

另外,因为链表在内存中是不连续的,所以它可以充分利用内存,同时,在内存空间足够的情况下,它的大小也是没有限制的,链表支持动态增长,而数组则是需要预先分配内存,可能会浪费掉一些内存资源(因为可能根本不会用到某些内存单元)

所以,其实链表还是相当有用滴   废话说了一些,开始码代码了

先看一下结构体的构成吧:

struct Node{
	int elem;
	Node *next;
	Node(){
		elem = 0;
		next = NULL;
	}
	Node(int elem_){
		elem = elem_;
		next = NULL;
	}
};


数据域只有一个int类型的数据elem,指针域为next,指向下一个节点; 默认构造函数将elem属性初始化为0,指向null,构造函数将elem属性初始化为参数elem_的值,指向的节点同样为null

首先讲一下如何创建链表
方法很简单,根据你现有的数据,创建新的Node节点,然后用next指针把链表和这些节点串联起来

Node *CreateNodeList(){
	/*
	func:根据指定数据创建链表
	return:创建完成之后的链表的头指针
	 */
	int data[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	Node *head = new Node();
	if(head == NULL){
		//内存分配失败
		return NULL;
	}else{
		head->elem = data[0];
		Node *temp, *currentHead;
		currentHead = head;
		for(int i = 1; i < 10; i++){
			temp = new Node(data[i]);
			currentHead->next = temp;
			//指针向后移一个单元
			currentHead = currentHead->next;
		}
		return head;
	}
}
现在开始讲一下插入操作,插入操作又分为几种,一种是链表头插入操作,一种是链表尾插入操作,一种是链表中间插入操作,

表头插入很简单,直接新建一个节点,指向当前的头节点就ok了

Node *InsertAtHead(Node *head, int elem){
 	/*
	func:在链表表头插入节点
	para:head为链表头指针,elem为要插入节点的数据成员
	return:插入操作完成之后的链表的头指针
	*/
	Node *newHead = new Node(elem);
	if(NULL == head){
		//这里实际上相当于新建了一个链表,表头为newHead
		return newHead;
	}else{
		newHead->next = head;
		return newHead;
	}	
}

表尾插入,需要将指针移动到列表尾,然后进行插入操作

Node *InsertAtTail(Node *head, int elem){
	/*
	func:在链表表尾插入节点
	para:head为链表头指针,elem为要插入节点的数据成员
	return:插入操作完成之后的链表的头指针
	 */	
	if(NULL == head)
		return NULL;
	else{
		Node *currentHead = head;
		Node *temp;
		//while循环使指针移动到链表尾
		while(currentHead->next){
			currentHead = currentHead->next;
		}
		temp = new Node(elem);
		currentHead->next = temp;
		return head;
	}
}
一般插入,这里模拟的是在一个有序链表中插入节点,使得插入之后,链表仍然有序,这种插入可能会遇到在表头插入或者在表尾插入的情况,所以我们要单独考虑这两种情况

Node *Insert(Node *head, int elem){
	/*
	funct:在升序链表中插入节点,使得节点插入完成之后链表仍然是升序
	para:head为链表头指针,elem为要插入节点的数据成员
	return:插入操作完成之后的链表头指针
	*/
	if(head == NULL){
		return head;
	}else{
		Node *currentHead = head;
		Node *temp = new Node(elem);
		if(elem < head->elem){
			//因为已经保证按升序插入,所以这种情况下直接在表头插入
			temp->next = head;
			return temp;
		}else{
			while(currentHead->next){
				int curElem = currentHead->elem;
				int nextElem = currentHead->next->elem;
				if(elem >= curElem && elem < nextElem){
					//在节点currentHead和currentHead->next之间插入temp
					temp->next = currentHead->next->next;
					currentHead->next = temp;
					break;
				}
				currentHead = currentHead->next;
			}
			//在表尾插入
			if(currentHead->next == NULL){
				currentHead->next = temp;
			}
			return head;
		}	
	}
}
删除节点同样有3个版本,在链表表头删除,在链表表尾删除,和在链表中间删除

首先是在链表表头删除节点,这个操作很简单,断开当前表头和链表的连接即可

Node *DeleteAtHead(Node *head){
	/*
	func:删除链表表头节点
	para:head为链表的头指针
	return:返回新链表的头指针
	*/
	Node *newHead = head->next;
	//head节点与链表断开
	head->next = NULL;
	return newHead;
}
然后是在链表表尾删除节点,同样很容易,只要将链表遍历一遍,最后断开尾节点跟前驱节点的连接即可

Node *DeleteAtTail(Node *head){
	/*
	func:删除链表表头节点
	para:head为链表的头指针
	return:返回新链表的头指针
	*/
	Node *currentHead = head;
	//前驱节点
	Node *prev;
	while(currentHead->next){
		prev = currentHead;
		currentHead = currentHead->next;
	}
	//经过while循环后,currentHead现在是尾节点,prev是它的前驱节点
	prev->next = NULL;
	return head;
}
现在再来说一下通过数据成员匹配的删除,这种情况下的删除需要记录下删除节点的前驱节点和下一个节点,然后让这两个节点连接起来,过程如下
Node *DeleteByElem(Node *head, int elem){
	/*
	funct:删除数据成员为elem的节点
	para:head为链表头指针,elem为要删除节点的数据成员值
	return:删除操作完成之后的链表头指针
	*/
	Node *currentHead = head;
	if(currentHead->elem == elem){
		head = head->next;
		currentHead->next = NULL;
		return head;
	}else{
		//prev用来记录当前的节点的前驱节点
		Node *prev = head;
		while(currentHead){
			if(currentHead->elem == elem){
				prev->next = currentHead->next;
				currentHead->next = NULL;
				break;
			}
			prev = currentHead;
			currentHead = currentHead->next;
		}
		return head;
	}

}
最后就是稍微复杂一点的链表反转操作,我的做法是这样的,每次让当前表的表头与剩下节点断开,然后插入到反转链表的表头,这样就完成了链表的反转

Node *RevearseNodeList(Node *head){
	/*
	funct:反转链表
	para:head为链表头指针
	return:反转操作完成之后的链表头指针
	*/	
	if(head == NULL || head->next == NULL){
		return head;
	}else{
		Node *currentHead = head->next;
		Node *temp;
		head->next = NULL;
		while(currentHead){
			//当前表的表头指向反转表的表头
			temp = currentHead->next;
			currentHead->next = head;
			//head移动到指向反转表的表头
			head = currentHead;
			currentHead = temp;
		}
		return head;
	}
}

好了,差不多就是这些了,不过最近看到网上很多人对于表头,表尾是否为空有一些疑问,其实表头跟表尾为空或者不为空都是可以的,我这里的实现,表头,表尾都用来存放数据

上面的代码都是经过调试的,如果有什么bug希望大家给我指出来,一起学习,一起进步~~










评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值