2.单链表以及相关代码

链式存储结构实现的线性表:链表

链式存储结构:逻辑上连续的数据,在存储时不连续,但是还得通过一定手段使得这些数据逻辑上连续:指针

指针指向:地址赋值

链表:基于指针实现的

  1. data 数据
  2. 下一个数据的地址:指针类型的变量

1+2=结点--->结构体类型

结构体:

链表:用指针把相互关联的结点连接在一起就形成了链表

链表:单(向)链表 双向链表 循环链表(循环链表和循环双链表)

相关概念

  1. 头指针:p,保存第一个结点地址的指针 就是头指针,

告诉了整个链表的指针(所以一般用头指针视为该链表的名字)

        2.首元结点:保存线性表第一个数据的结点 就是首元结点

        3.头结点:一个不存放数据,仅仅存放首元结点地址的结点

 

 

首元结点和头结点是不是同一个结点?----不是

 有头结点的第二个才是首元结点 没有头结点的 第一个就是首元结点

为什么带头结点:使得首元结点就不特殊了,更改后面的结点更容易(使首元结点一起更改)

不然还要单独修改首元结点的数据域,为了使首元结点和后面结点的操作统一起来

遍历链表:

Node *p=L;(相当于增加一个头指针,然后利用这个指针来遍历)

While(p!=NULL)

p=p->next;

为什么不用Node s(变量名来申请空间而是要用malloc直接申请)

因为Node s之后 s为这个结构体的变量名,在链表中用不着变量名,因为每个结点保存的是下一个结点的地址,所以用变量名的话,找地址的时候还得加取地址符号(&),所以直接用malloc来取空间,得到的就是地址,很方便直接用这个地址取赋值了,所以一般不用数据类型直接声明变量而是用malloc申请空间用指针来指向它

/操作:增删改查

增:头插法 尾插法 中间插入

头插法:固定在头结点后插入数据(结点),新插入的结点做首元结点

尾插法:在最后一个结点后面插入数据,新插入的结点做最后一个结点(尾结点)

中间插入:在指定结点之后插入数据,涉及查找函数

查找:找到指定数据所在的结点(遍历,找到数据域相等的结点)

改:指定数据改成某个数据

删除:删除指定数据

  1. 判断是否为空链表
  2. 查找:这里有区别:需要找到对应结点的前面一个结点,即需要增加一个辅助结点(把p移动下一个结点,把q再移动到p结点这样动)

链表:灵活,空间限制

缺点:不能随机访问

#include<stdio.h>
#include<stdlib.h>
//结点:结构体类型
typedef struct Node {
	int data;//数据域
	struct Node* next;//指针 
}Node, * linklist;
//linklist p;这种写法代表p是结构体指针类型的 
//linlist == Node*  为了增强代码的可读性 
//linklist 强调该指针是头指针,通过保存头结点地址,标记了整个链表
//Node* q 强调该指针指向一个普通的结点 
//初始化一个带头结点空链表 
linklist initlist()//返回值是头指针所以类型是linklist类型的 
{
	//申请一个头结点空间,把地址赋值给头指针
	linklist l = (Node*)malloc(sizeof(Node));
	if (l == NULL)
	{
		printf("内存分配不成功\n");
	}
	else
	{
		l->next = NULL;
	}
	return l;
}
linklist head_insert(linklist l, int k)
{
	Node* s = (Node*)malloc(sizeof(Node));
	//判断空间会不会申请失败----略 
	s->data = k;
	s->next = l->next;//重要的是里面的数据 (l->next里面的结点数据传给了s->next) 
	l->next = s;//之后再把新结点放入l->next后面 
	return l;
}
linklist tail_insert(linklist l, int k)
{
	//找尾结点---先声明一个指针来辅助遍历找到最后一个结点 
	Node* p = l;
	while (p->next != NULL)
	{
		p = p->next;
	}
	//找到后申请结点 
	Node* s = (Node*)malloc(sizeof(Node));
	s->data = k;
	s->next = NULL;
	p->next = s;
	return l;
}
//查找要找到结点,所以需要返回的是结点的地址即指针 
Node* find(linklist l, int x)
{
	Node* p = l->next;//从首元结点开始 
	//判断x数据是否存在(因为x是在结点p里面的,即判断p结点不为空)
	while (p != NULL && p->data != x)
	{
		p = p->next;
	}
	return p;
}
linklist mid_insert(linklist l, int x, int k)
{//在x后面插入k 
	Node* p = find(l, x);
	if (p == NULL)
	{
		printf("数据x不存在,无法插入\n");
	}
	else
	{
		Node* s = (Node*)malloc(sizeof(Node));
		s->data = k;
		s->next = p->next;
		p->next = s;
	}
	return l;
}
linklist remo(linklist l, int x, int k)
{
	Node* p = find(l, x);
	if (p == NULL)
	{
		printf("数据x不存在,无法修改\n");
	}
	else
	{
		p->data = k;
	}
	return l;
}
linklist delet(linklist l, int k)
{
	//判断是否为空链表 
	if (l->next == NULL)
	{
		printf("空链表\n");
	}
	else
	{
		//查找:
		Node* p = l->next; //p从首元结点开始找 
		Node* q = l;//q为p的前一个结点 
		while (p != NULL && p->data != k)
		{
			q = p;
			p = p->next;
		}
		q->next = p->next;
		free(p);
		p = NULL;//防止p成为野指针 
	}
	return l;
}
删除另一个写法
//linklist delet1(linklist l, int k)
//{
//	if (l->next == NULL)
//	{
//		printf("空链表\n");
//	}
//	else
//	{
//		Node* q = l;
//		Node* p;//声明指针p 
//		while (q->next != NULL && q->next->data != k)
//		{
//			q = q->next;
//		}
//		q = q->next->next;
//		//防止q->next为野指针 所以另把它赋值为q然后free掉
//		p = q->next;
//		free(p);
//	}
//	return l;
//}
void show(linklist l)
{
	Node* p = l->next;//从首元结点开始
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");

}
int main()
{
	linklist l = initlist();//声明一个头指针 用初始化函数来初始化链表 
	//插入结点 增:头插法 尾插法 中间插入 
	//头插法 插入顺序为逆序的 
	l = head_insert(l, 1);//可不可以写成head_insert(&l,1); 
	l = head_insert(l, 4);
	l = head_insert(l, 2);
	l = head_insert(l, 6);
	show(l);


	//尾插法 插入顺序为正序 但需要定义一个指针来遍历找到尾结点( 尾结点特征:最后next指向NULL)
	l = tail_insert(l, 7);
	show(l);
	//中间插法 在4数据后面插入数据9 
	l = mid_insert(l, 4, 9);
	show(l);
	//改
	l = remo(l, 2, 3);
	show(l);
	//删除(先写,再去写函数内部) 
	l = delet(l, 6);
	//l = delet1(l, 6);//里面指针有报错的地方 
	show(l);
	return 0;
}

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是代码实现: c1.h: ```c typedef struct LNode { Complex data; struct LNode *next; } LNode, *LinkList; ``` c2-2.h: ```c void CreateList(LinkList *L, int n); void SortList(LinkList L); void PrintList(LinkList L); ``` bo2-2.c: ```c #include <stdio.h> #include <stdlib.h> #include "c1.h" #include "c2-2.h" void CreateList(LinkList *L, int n) { *L = (LinkList) malloc(sizeof(LNode)); (*L)->next = NULL; LinkList p = *L; for (int i = 0; i < n; i++) { LinkList q = (LinkList) malloc(sizeof(LNode)); scanf("%lf%lf", &q->data.real, &q->data.imag); q->next = NULL; p->next = q; p = q; } } void SortList(LinkList L) { int len = 0; for (LinkList p = L->next; p != NULL; p = p->next) { len++; } for (int i = 0; i < len - 1; i++) { LinkList p = L->next; for (int j = 0; j < len - i - 1; j++) { if (p->data.real * p->data.real + p->data.imag * p->data.imag > p->next->data.real * p->next->data.real + p->next->data.imag * p->next->data.imag) { Complex tmp = p->data; p->data = p->next->data; p->next->data = tmp; } p = p->next; } } } void PrintList(LinkList L) { for (LinkList p = L->next; p != NULL; p = p->next) { printf("%.2lf+%.2lfi ", p->data.real, p->data.imag); } printf("\n"); } ``` main2-2.c: ```c #include <stdio.h> #include "c1.h" #include "c2-2.h" int main() { LinkList L; int n; scanf("%d", &n); CreateList(&L, n); SortList(L); PrintList(L); return 0; } ``` 输入格式为: ``` n a1 b1 a2 b2 ... an bn ``` 其中 n 表示复数个数,ai 和 bi 分别表示第 i 个复数的实部和虚部。 输出格式为: 按模从小到大排列后的复数列表,每个复数的实部和虚部之间用加号连接,复数之间用空格分隔。 例如,输入: ``` 3 1 2 3 4 5 6 ``` 输出: ``` 1.00+2.00i 3.00+4.00i 5.00+6.00i ``` 注意,这里的输出结果是按照模从小到大排列的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值