单链表(c语言版)

41 篇文章 0 订阅
17 篇文章 0 订阅

升级版(推荐)

  • 为了解决前面传统方法(顺序存储)中插入和删除需要移动大量的元素,因为耗费时间,所以采用链式存储来解决问题:让所有的元素都不相邻,不连续,可以在内存中的任意位置。只找到当前节点和下一个节点的位置即可,这样就可以直接插入而不移动元素。
  • 节点(node):包含:数据域,指针域。指针域中存储的信息为指针或链。由n个节点链接成应该表–>线性表。
  • 我们把链表中第一个节点(头节点)存储的位置叫做头指针。
  • 在单链表前设一个头节点,不存储任何个人私有数据信息,但存储一些公共信息:线性表的长度等等。

头节点和头指针的区别

头指针
  • 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。
  • 头指针具有标识作用,所以常用头指针冠以链表的名字。
  • 无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
头节点
  • 头结点是为了操作的统一和方便而设立的,放在第一元素的结点之前,其数据域一般无意义(也可存放链表的长度)。
  • 有了头结点,对在第一元素结点前插入结点和删除第一结点,其操作与其它结点的操作就统一了。
  • 头结点不定是链表必须要素
    在这里插入图片描述

插入

  • 如图所示:
  • 代码从后往前赋值。
  • 表中间的情况:
s->next = p->next;
p->next = s;

在这里插入图片描述

  • 表头和表尾的情况:
    在这里插入图片描述

删除

list* temp = p->next;
p->next = temp->next;
mystruct* data = &temp->data;
free(temp);

在这里插入图片描述

创建n个元素的链表

头插法
typedef struct List
{
	Data data;
	List* next;
}list;

//插入部分的代码:
void(list* old,Data data_)//old为传出参数
{
	......
	//开辟一个头节点
	list* l = (list*)malloc(sizeof(list));
	l->next = NULL;
	
	//开辟新空间存放数据,可以循环创建for
	list* input = (list*)malloc(sizeof(list));
	input->data = data;
	input->next = l->next;
	l->next = input;
	old = l;//old为传出参数
}
尾插法
  • 要创建一个尾节点,并随链表的加入而不断变化。
void(list* old,Data data_)
{
	list* behind = NULL;
	old = (list*)malloc(sizeof(list));
	behind = old;
	
	//插入元素,可以for循环插入
	list* input = (list*)malloc(sizeof(list));
	list->data = data_;
	behind->next = input;//新节点插入到上一个节点尾部
	behind = input;//更新尾节点的位置
	behind->next = NULL;
}

实例(歌曲管理):

#include<stdio.h>
#include<malloc.h>
#include<string.h>
#include<stdlib.h>

typedef struct Node
{
	//定义连接链表的节点:
	Node* next;
}node;

typedef struct Common_list
{
	//定义一个空间域
	node head;
	int length;
}common_list;

//1. 建立空链表:
common_list* creat_list()
{
	common_list*temp_creat;
	temp_creat = (common_list*)malloc(sizeof(common_list));
	if (temp_creat == nullptr)
	{
		printf("空间开辟错误!!!\n");
		return nullptr;
	}
	printf("已成功创建空列表!!1\n");
	temp_creat->head.next = nullptr;
	temp_creat->length = 0;
	printf("空列表初始化成功!!1\n");
	return temp_creat;
}

//2. 开始在old中的pos位置处插入元素input:
void insert_list(common_list**old,node*input, int pos)
{
	common_list*temp_insert = *old;
	if (temp_insert == nullptr)
	{
		printf("传入空间错误!!!\n\n");
		return ;
	}
	else if (pos<0)
	{
		printf("位置号不能小于0");
		return;
	}

	else if (pos > temp_insert->length)
	{
		pos = temp_insert->length;
	}
	//定义一个辅助指针:
	node*current = &temp_insert->head;
	for (int i = 0; i < pos; i++)
	{
		//开始移动到插入的位置:
		current = current->next;
	}
	//进行插入操作:
	input->next = current->next;
	current->next = input;
	temp_insert->length++;
	printf("歌曲插入成功!!!\n\n");
}

//查找pos处的元素:
node* find_list(common_list*old, int pos)
{
	common_list*temp_find = old;
	if (temp_find == nullptr)
	{
		printf("传入空间错误!!!\n\n");
		return nullptr;
	}
	else if (pos < 0 || pos>=temp_find->length)
	{
		printf("该位置处无歌曲!!!\n\n");
		return nullptr;
	}

	//定义一个辅助指针便于寻找:
	node* current = temp_find->head.next;
	for (int i = 0; i < pos; i++)
	{
		current = current->next;
	}
	printf("找到歌曲啦!!!\n\n");
	return current;
}

//定义删除函数:
void delete_list(common_list**old, int pos)
{
	common_list*temp_del = *old;
	if (temp_del == nullptr)
	{
		printf("传入空间错误!!!\n\n");
		return ;
	}
	else if (pos < 0 || pos >= temp_del->length)
	{
		printf("该位置处无歌曲!!!\n\n");
		return ;
	}

	//定义一个辅助指针定位当前位置:
	node* current = &temp_del->head;
	for (int i = 0; i < pos; i++)
	{
		current = current->next;
	}
	//定义一个辅助指针用于缓存要删除的节点:
	node*del = current->next;
	current->next = del->next;
	temp_del->length--;
	printf("歌曲删除成功!!!\n\n");
}

void clear_list(common_list**old)
{
	common_list* temp_cls = *old;
	if (temp_cls == nullptr)
	{
		printf("传入空间错误!!!\n\n");
		return;
	}
	free(temp_cls);
	temp_cls = nullptr;
	printf("歌曲已经清空!!!\n\n");
}

//定义一个用户数据域:
typedef struct User
{
	//用于链接数据的节点:
	node head;

	//用户数据:
	char song_name[16];
	int song_time;
	char star[32];
}user;

int main()
{
	common_list*old = creat_list();
	user* get;

	user u2;
	strcpy_s(u2.star, "周杰伦");
	strcpy_s(u2.song_name,"告白气球");
	u2.song_time = 111;
	insert_list(&old,(node*)&u2,0);

	user u3;
	strcpy_s(u3.star, "周杰伦");
	strcpy_s(u3.song_name, "mojito");
	u3.song_time = 222;
	insert_list(&old, (node*)&u3, 0);

	user u1;
	strcpy_s(u1.star, "夏婉安");
	strcpy_s(u1.song_name, "新年好");
	u1.song_time = 333;
	insert_list(&old, (node*)&u1, 0);

	user u4;
	strcpy_s(u4.star, "格子兮");
	strcpy_s(u4.song_name, "秋殇别恋");
	u4.song_time = 444;
	insert_list(&old, (node*)&u4, 8);
	for (int i = 0; i < old->length; i++)
	{
		get = (user*)find_list(old, i);
		printf("你的歌曲如下:\n歌手:%s,歌曲:%s,时间长:%d\n\n",get->star,get->song_name,get->song_time);
	}
	
	delete_list(&old,0);
	for (int i = 0; i < old->length; i++)
	{
		get = (user*)find_list(old, i);
		printf("你的歌曲如下:\n歌手:%s,歌曲:%s,时间长:%d\n\n", get->star, get->song_name, get->song_time);
	}

	return 0;
}

模拟手机类型管理(单链表传统版)

#include<stdio.h>
#include<malloc.h>
#include<string.h>
#include<stdlib.h>
typedef struct Phone
{
	int price;
	char brand[8];
	//int price2;
	struct Phone *next;
}phone;

phone *insert(phone *old,phone *new_)
{
	if (old == NULL)
	{
		printf("这个是空链表,开始插入:\n");
		old = new_;
		new_->next = NULL;
	}
	else
	{
		printf("这个不是空链表,继续插入:\n");
		while (old->next != NULL)
		{
			old = old->next;
		}
		old->next = new_;
		new_->next = NULL;
	}
	return old;
}

void insert(phone *old,phone *new_,int pos)
{
	
}

void show(phone *old)
{
	int i = 0;
	if (old == NULL)
	{
		printf("链表为空......\n");
		return;
	}
	while(old!=NULL)
	{
		printf("价格为:%d, 型号为:%s\n",old->price,old->brand);
		old = old->next;
	}
}

void search(phone* old, int price)
{
	if (old == NULL)
	{
		printf("链表为空......\n");
		return;
	}
	while (old !=NULL)
	{
		if (old->price == price)
		{
			printf("该手机存在:%s,价格为:%d\n",old->brand,old->price);
			return;
		}
		old = old->next;
	}
	printf("未找到!!!\n");
}

phone* delete_(phone* old_, int price)
{
	phone* old = old_;
	phone* new_ = old_;
	if (new_ == NULL)
	{
		printf("链表为空......\n");
		return NULL;
	}
	if (old->price == price)
	{
		printf("%s 手机存在,正在删除!!!\n", old->brand);
		old = old->next;
		return old;
	}
	while (new_ !=NULL)
	{
		old = old->next;

		if (old->price == price && old !=NULL)
		{
			printf("%s 手机存在,正在删除!!!\n",old->brand);
			new_->next = old->next;
			return old;
		}
		new_ = new_->next;
	}
	printf("手机不存在,删除失败!!!\n");
	return new_;
}
int main()
{
	phone *old, *input;
	old = NULL;
	input = NULL;

	input = (phone*)malloc(sizeof(phone));
	input->price = 3999;
	strcpy_s(input->brand,"iQOO7");
	old = insert(old,input);
	show(old);

	input = (phone*)malloc(sizeof(phone));
	input->price = 4999;
	strcpy_s(input->brand,"iQOO5");
	insert(old, input);
	show(old);

	input = (phone*)malloc(sizeof(phone));
	input->price = 5999;
	strcpy_s(input->brand,"iQOO3");
	insert(old, input);
	show(old);

	input = (phone*)malloc(sizeof(phone));
	input->price = 6999;
	strcpy_s(input->brand, "iQOO");
	insert(old, input);
	show(old);

	search(old,5999);
	search(old, 3999);
	search(old, 599);

	printf("删除前:\n");
	show(old);
	delete_(old,5999);
	show(old);

	old = delete_(old, 3999);
	show(old);

	delete_(old, 6999);
	show(old);

	old = delete_(old, 4999);
	show(old);

	/*
for (int i = 0; i < 4; i++)
{
	input = (phone*)malloc(sizeof(phone));
	input->price = 3999+i;
	strcpy_s(input->brand,"iQOO7");
	old = insert(old, input);
	show(old);
	system("pause");
}
*/
	return 0;
}

运行结果:

在这里插入图片描述

模拟年龄管理(顺序存储,进阶版)

地址计算的方法

  • 存储器中的每个存储单元有自己的编号,称为地址
  • 假设每个数据元素占用c个单元,则第i和i+1的位置关系是:loc(i+1)=loc(i)+c。
#include<iostream>

typedef struct Song
{
	int capacity;
	int length;//线性表当前长度
	int *node;//数组存储元素,可动态分配大小
	//线性表的长度应该小于等于数组的长度
	
}song;

song* list_creat(int capacity)
{
	song* old = NULL;
	old = (song*)malloc(sizeof(song));

	old->node = (int*)malloc(sizeof(int)*capacity);
	old->length = 0;
	old->capacity = capacity;

	return old;
}

//插入元素:
void insert(void *old_, void* input, int pos)
{
	song* old = NULL;
	if (old_ == NULL || input ==NULL || pos<0)
	{
		printf("输入错误!!!\n");
		return;
	}
	old = (song*)old_;
	if (old->length >= old->capacity)
	{
		printf("链表满了!!!\n");
		return;
	}
	else if (pos>old->length)
	{
		pos = old->length;
	}
	for (int i = pos; i < old->length; i++)
	{
	//在中途插入,在此后面的元素通通向后移
		old->node[pos + 1] = old->node[pos];
	}
	//然后直接插入对应位置
	old->node[pos] = (int)input;
	old->length++;
}

//获取元素:
void* list_get(song* old, int pos)
{
	if (old == NULL || pos<0)
	{
		printf("链表错误!!!\n");
		return NULL;
	}
	return (void*)old->node[pos];
	//直接返回对应的位置即可
}

void* delete_(song* old, int pos)
{
	if (old == NULL )
	{
		printf("链表错误!!!\n");
		return NULL;
	}
	else if (pos<0 || pos>=old->length)
	{
		printf("删除位置错误!!!\n");
		return NULL;
	}
	
	//找到对应位置,后面的值往前覆盖
	//printf("%d 号元素已经删除!!!\n", *(int*)old->node[pos]);
	void* temp = (void*)old->node[pos];
	
	for (int i = pos; i < old->length; i++)
	{
		old->node[i] = old->node[i + 1];
	}
	old->length--;
	return temp;
}

int get_length(song*old)
{
	if (old == NULL)
	{
		printf("链表错误!!!\n");
		return NULL;
	}
	return (old->length)>=0? old->length:0;
}

void clear_list(song*old)
{
	old->capacity = 0;
	old->length = 0;
	free( old);
	old = NULL;
	printf("链表已经清空!!!\n");
}
struct Star
{
	int age;
}star1,star2,star3,star4;

struct Star1
{
	int age;
}star_1, star_2, star_3, star_4;
int main()
{
	song* s = NULL;
	s = list_creat(8);

	star1.age = 11;
	star2.age = 21;
	star3.age = 31;
	star4.age = 41;
	insert(s,(void*)&star1,0);
	insert(s, (void*)&star2, 1);
	insert(s, (void*)&star3, 2);
	insert(s, (void*)&star4, 3);

	star_1.age = 1;
	star_2.age = 2;
	star_3.age = 3;
	star_4.age = 4;
	insert(s, (void*)&star_1, 4);
	insert(s, (void*)&star_2, 5);
	insert(s, (void*)&star_3, 6);
	insert(s, (void*)&star_4, 7);

	int len = get_length(s);
	for (int i = 0; i < len; i++)
	{
		printf("第【%d】个元素是:%d\n", i, *(int *)list_get(s, i));
	}

	printf("删除后:\n");
	delete_(s,0);
	delete_(s, 2);
	delete_(s, 4);
	delete_(s, 6);

	len = get_length(s);
	for (int i = 0; i < len; i++)
	{
		printf("第【%d】个元素是:%d\n",i,*(int *)list_get(s,i));
	}

	clear_list(s);
	printf("链表的长度是:%d\n",get_length(s));
	return 0;
}

运行结果:

在这里插入图片描述

总结

顺序结构:

  • 线性表需要频繁查找时使用。

单链表:

  • 线性表频繁插入和删除时使用,表中的元素变化较大时使用,因为这样不需要考虑存储空间大小的问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值