链表的简单算法

1、链表是用来干嘛的

链表就是用来解决数组的大小不能动态扩展的问题,所以链表其实就是当数组用的。直白点:链表能完成的任务用数组也能完成,数组能完成的任务用链表也能完成。但是灵活性不一样。
链表就是用来存储数据的。链表用来存数据相对于数组来说优点就是灵活性,需要多少个动态分配多少个,不占用额外的内存。数组的优势是使用简单(简单粗暴)。

2、单链表的实现

(1)链表的内存要求比较灵活,不能用栈,也不能用data数据段。只能用堆内存
(2)使用堆内存来创建一个链表节点的步骤:
1、申请堆内存,大小为一个节点的大小(检查申请结果是否正确);
2、清理申请到的堆内存;
3、把申请到的堆内存当作一个新节点;
4、填充你哦个新节点的有效数据和指针区域。

#include <stdio.h>

//定义一个节点
struct node
{
	int data;
	struct node *pNext;
};


int main(void)
{
	//创建头指针
	struct node *pHead = NULL;
	//创建一个节点
	struct node *p1 = (struct node *)malloc(sizeof(struct node));
	if(NULL == p1)
	{
		printf("malloc error.\n");
		return -1;
	}
	//清内存
	memset(p1, 0, sizeof(struct node));
	
	p1 -> data = 1;
	p1 -> pNext = NULL;
	
	return 0;
}

创建新节点函数

struct node *creat_node(int DATA)
{
	struct node *p = (struct node *)malloc(sizeof(struct node));
	if(NULL == p)
	{
		printf("malloc error.\n");
		return NULL;
	}
	
	//清除堆内存
	memset(p, 0, sizeof(struct node));
	
	//对节点赋值
	p->data = DATA; 
	p->pNext = NULL;
	
	return p;
}

3、单链表的算法——插入节点

头节点在创建头指针时一并创建并且和头指针关联起来;
后面的真正的存储数据的节点用节点添加的函数来完成
头节点的特点是:
第一,它紧跟在头指针后面。
第二,头节点的数据部分是空的(有时候不是空的,而是存储整个链表的节点数

1、从链表尾部插入新节点

void insert_tail(struct node *pH, struct node *new)
{
	int cnt = 0;
	struct node *p = pH;
	//找到最后一个节点
	while(NULL != p->pNext)
	{
		p = p->pNext;
		cnt++;
	}
	//让最后一个节点指向新节点
	p->pNext = new;
	//节点数加一
	pH->data = cnt + 1;
}

2、从链表头部插入新节点

void insert_head(struct node *pH, struct node *new)
{
	//让新节点的指针指向头节点指向的节点
	new->pNext = pH->pNext;
	
	//头节点指向新节点
	pH->pNext = new;
	
	//节点数加一
	pH->data += 1;
}

4、单链表的算法——遍历节点

void linglist_traverse(struct node *pH)
{
	struct node *p = pH;
	while (NULL != p->pNext)
	{
		p = p->pNext;	//指针移到下一个节点
		printf("struct node data = %d.\n", p->data);
	}
	
}

5、单链表的算法——删除节点

1、删除节点的2个步骤

第一步:找到要删除的节点;
第二步:删除这个节点。

2、删除节点的2种情况

(1)待删除的节点不是尾节点的情况: 首先把待删除的节点的前一个节点的pNext指针指向待删除的节点的后一个节点的首地址(这样就把这个节点从链表中摘出来了),然后再将这个摘出来的节点free掉接口。

(2)待删除的节点是尾节点的情况: 首先把待删除的尾节点的前一个节点的pNext指针指向null(这时候就相当于原来尾节点前面的一个节点变成了新的尾节点),然后将摘出来的节点free掉。

注意堆内存的释放
(1)当程序都结束了的情况下那些没有free的堆内存也被释放了。
(2)有时候我们的程序运行时间很久,这时候malloc的内存如果没有free会一直被占用直到你free释放它或者整个程序终止。

//删除节点数据为Data的节点
//第一步:找到要删除的节点;
//第二步:删除这个节点。
int delete_node(struct node *pH, int Data)
{
	struct node *pPrev = NULL;
	struct node *p = pH;
	while (NULL != p->pNext)
	{
		pPrev = p;		//保存当前节点
		p = p->pNext;	//指针移到下一个节点
		if(p->data == Data)		//找到相同数据的节点
		{
			//这个节点是尾节点
			if(NULL == p->pNext)
			{
				pPrev->pNext = NULL;//前一个节点指向NULL
				free(p);			//释放内存
			}
			//这个节点不是尾节点
			else
			{
				pPrev->pNext = p->pNext;
				
				free(p);
			}
//			return 0;
		}
	}
//	printf("not find that node\n");
//	return -1;
}

6、单链表的算法——逆序

链表逆序 = 遍历 + 头插入

//链表逆序 = 遍历 + 头插入
void linklist_reverse(struct node *pH)
{
	struct node *p = pH->pNext;//pH指向头节点,P指向第一个有效节点
	struct node *pBack = NULL;
	
	//当没有有效节点和只有一个有效节点时,不需要任何操作
	if((NULL==p) || (NULL==p->pNext))
	{
		return;
	}
	
	//有两个以上的有效节点时
	while (NULL != p)//找到最后一个节点,遍历
	{
		pBack = p->pNext;	//保存p的下一节点地址
		if(p == pH->pNext)	//判断是否为第一个节点
		{
			p->pNext = NULL;
		}
		else
		{
			p->pNext = pH->pNext;//指向第二 = 指向第一
		}
		pH->pNext = p;			//挂接前面的节点
		p = pBack;				//往后移一位
	}
	//insert_head(pH, p);
}

7、双向链表的引入和基本实现

双向链表的节点 = 有效数据 + 2个指针(一个指向后一个节点,另一个指向前一个节点)

struct node 
{
	int data;
	struct node *pPrev;//前指针,指向前一个节点
	struct node *pNext;//指向后一个节点
};

struct node *creat_node(int Data)
{
	//在堆内存中定义一个节点p
	struct node *p = (struct node *)malloc(sizeof(struct node));
	if(NULL == p)
	{
		printf("mallco error\n");
		return NULL;
	}
	
	//申请内存清0
	memset(p, 0, sizeof(struct node));
	
	//节点赋值
	p->data = Data;
	p->pPrev = NULL;
	p->pNext = NULL;
}

8、双向链表的算法——插入节点

尾部插入

void insert_tail(struct node *pH, struct node *new)
{
	struct node *p = pH;
	while(NULL != p->pNext)//找到最后一个节点
	{
		p = p->pNext;		//往后走一步
	}
	p->pNext = new;			//前一个结点的pNext与新节点连接
	new->pPrev = p;			//新节点的pPrev与当前节点连接

}

头部插入

void insert_head(struct node *pH, struct node *new)
{
	struct node *p = pH->pNext;

		//尾部操作
		//新节点的pNext指向第一个有效节点
		new->pNext = pH->pNext;
		//头节点的pNext指向新节点
		pH->pNext = new;
		
		//头部操作
		//原来的有效节点的pPrev指向新节点
		if(NULL != p)//当没有有效节点时不操作
			p->pPrev = new;
		//新节点的pPrev指向头节点
		new->pPrev = pH;
}

9、双向链表的算法——遍历

向后遍历

//向后遍历,出入头指针
void double_linklist_traverse_latter(struct node *pH)
{
	struct node *p = pH;
	while (NULL != p->pNext)
	{
		p = p->pNext;
		printf("strucr node data = %d\n", p->data);
	}
}

向前遍历

//向前遍历,传入尾指针
void double_linklist_traverse_before(struct node *pTail)
{
	struct node *p = pTail;
	while (NULL != p->pPrev)
	{
		printf("strucr node data = %d\n", p->data);
		p = p->pPrev;
	}
}

10、双向链表的算法——删除节点

int double_linklist_delete_node(struct node *pH, int Data)
{
	struct node *p = pH;
	while(NULL != p->pNext)
	{
		p = p->pNext;
		if(p->data == Data)//找到数据和Data相同的节点
		{
			if(NULL == p->pNext)//这个节点是尾节点时
			{
				pH->pNext = NULL;
			}
			else//为普通节点
			{
				p->pPrev->pNext = p->pNext;
				p->pNext = p->pPrev;
			}
			
			free(p);
			
			return 0;
		}
		
		printf("没有相同的数据\n");
		return -1;
	}
	
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值