离散存储【链表】
1.定义:
n个结点离散分配彼此通过指针相连
每个结点只有一个前驱结点,每个结点只有一个后续结点
首结点没有前驱结点,尾结点没有后续结点
2.专业术语:
首结点:第一个存放有效数据的结点
尾结点:最有一个存放有效数据的结点
头结点:头结点的数据类型和首结点的类型是一样的
首结点之前的结点// 第一个有效结点之前的那个指针
头结点并不存放有效数据,也没有存放整个链表中 结点的个数。
加头结点的目的是为了方便对链表的操作
头指针:指向头结点的指针变量 链表的首位置
尾指针:指向尾结点的指针变量
示例1:如何定义一个结点(每一个结点的类型如何表示)
typedef struct Node
{
int data; //数据域
struct Node * pNext; //指针域
//指针存储的地址只能是 struct Node 类型。
//指针域指向的 是与 本身结点数据类型相同的下一个结点 所以数据类型是 struct Node *;
}NODE,*PNODE;
//NODE 等价于 struct Node,
//PNODE 等价于 struct Node *;
3.如果希望一个函数来对链表进行处理,我们至少需要接受链表的哪些信息:
只需要一个参数:头指针以为我们通过头指针可以推算出链表的其他所有参数
4.分类:
单链表:每个结点的指针域只指向后面的结点双链表:每一个结点有两个指针(指前指后)
循环链表:能通过任何一个结点找到其他所有的结点
非循环链表
5.算法:
遍历查找
清空
销毁
求长度
排序
删除结点
插入结点
插入结点:(伪算法)
//第1种写法
r = p->pNext;
p->pNext = q;
q->pNext = r;
//第2种写法
q->pNext = p->pNext;
p->pNext = q;//(这两行代码不能倒过来)
/*
q 本身就是结点的地址 q 指向的就是 结点
q-> pNext 表示的就是结点的指针域
至于指针域中存放的下一个结点的地址是谁 稍加判断就好了。
最重要的!p->pNext //p所指向的结构体变量中 pNext 成员本身。
*/
删除结点:(伪算法)
r = p->pNext;
p->pNext = p->pNext->pNext;
free(r);//这里很重要 一定要记得释放内存,防止内存 溢出
示例2:创建并遍历一个链表,实现判断是否为空,求链表的长度查找删除
仿JAVA中LinkedList对象部分功能的实现
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
typedef struct Node
{
//数据域
int data;
//指针域
struct Node * pNext;
//指针域指向的 是与 本身结点数据类型相同的下一个结点 所以数据类型是 struct Node *;
}NODE,*PNODE;
//NODE 等价于 struct Node,
//PNODE 等价于 struct Node *;
//函数声明
PNODE create_list(void);
void traverse_list(PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE);
bool insert_list(PNODE,int,int);
bool delete_list(PNODE,int,int *);
void sort_list(PNODE);
int main(void)
{
PNODE pHead = NULL;//等价于 struct Node * pHead = NULL;
//create_list()的功能:创建一个非循环单链表,并将该链表的头结点的地址赋给 pHead
pHead = create_list();
traverse_list(pHead);//遍历
if(is_empty(pHead))
printf("链表为空\n");
else
printf("链表不空\n");
int len = length_list(pHead);
printf("链表的长度是%d \n",len);
sort_list(pHead);
traverse_list(pHead);//遍历
insert_list(pHead,2,33);
traverse_list(pHead);//遍历
int val;
if(delete_list(pHead,4,&val))
{
printf("您删除的元素是%d\n",val);
}
else
{
printf("删除失败%d\n",val);
}
return 0;
}
//创建一个链表,创建一个非循环单链表,并将该链表的头结点的地址赋给 pHead
PNODE create_list(void)
{
//C语言里的写法是把所有的变量都定义在前面
int len;//用来存放有效结点的个数
int i;
int val;//用来存放用户输入的有效结点的值
//升成一个头结点 头结点的数据类型与 其他结点是相同的。
//分配了一个不存放有效数据的头结点
PNODE pHead = (PNODE)malloc(sizeof(NODE));
if(NULL == pHead)
{
printf("分配失败,程序终止。\n");
exit(-1);
}
//pTail 永远指向 尾结点
PNODE pTail = pHead;
pTail -> pNext = NULL;
printf("请输入您需要生成的链表的结点的个数:len =");
scanf("%d",&len);
for(i =0;i<len;++i)
{
printf("请输入第%d个结点的值:",i+1);
scanf("%d",&val);
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if(NULL == pNew)
{
printf("分配失败,程序终止。\n");
exit(-1);
}
pNew ->data = val;
//新生成的结点要挂到整个链表的最后的一个位置。
pTail -> pNext = pNew;
pNew ->pNext = NULL;
pTail = pNew;
}
return pHead;
}
//遍历这个链表
void traverse_list(PNODE pHead)
{
//p 指向的是链表的第一个有效结点
PNODE p = pHead->pNext;
while(p!= NULL)
{
printf(" %d",p->data);
p = p->pNext;
}
printf("\n");
return;
}
//判断链表是否为空
bool is_empty(PNODE pHead)
{
if(pHead->pNext == NULL)
return true;
else
return false;
}
//返回链表长度
int length_list(PNODE pHead)
{
PNODE p = pHead -> pNext;
int len = 0;
while(p != NULL)
{
len++;
p = p->pNext;
}
return len;
}
//链表排序
void sort_list(PNODE pHead)
{
//实际上算法都是类似
//数组和链表同样都是 线性结构
int i,j,t;
PNODE p, q;
int len = length_list(pHead);
//i 是数组中第一个有效元素的下标 p 是链表中第一个有效元素的 地址。
for(i = 0,p = pHead ->pNext;i<len - 1;++i,p = p->pNext)
{
// q 就应该是p 的下一个元素
for(j = i+1,q = p ->pNext;j<len;++j,q = q->pNext)
{
/*
请参照数组中的方式来理解 链表
if(a[i] > a[j])
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
*/
if((p->data )> (q->data))
{
t = p->data;
p->data = q->data;
q->data= t;
}
}
}
return;
}
//在pHead 所指向链表的第pos 个结点的前面插入一个新的结点,该结点的值是 val并且 pos 的值是从1 开始。
bool insert_list(PNODE pHead,int pos,int val)
{
int i = 0;
PNODE p = pHead;
while(p != NULL && i<pos -1)
{
p = p->pNext;
i++;
}
if(i>pos-1||p== NULL)
return false;
PNODE pNew = (PNODE)malloc(sizeof(PNODE));
if(pNew == NULL)
{
printf("动态分配内存失败");
exit(-1);
}
pNew ->data = val;
//定义了一个临时结点
PNODE q = p->pNext;
p->pNext = pNew;
pNew ->pNext = q;
return true;
}
//删除指定位置的元素,并用*pVal记录被删除的元素
bool delete_list(PNODE pHead,int pos,int * pVal)
{
int i = 0;
PNODE p = pHead;
while(p ->pNext!= NULL && i<pos -1)
{
p = p->pNext;
i++;
}
if(i>pos-1||p ->pNext== NULL)
return false;
PNODE q = p ->pNext;
*pVal = q ->pNext;
//删除p结点后面的结点
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}