链表的定义:
线性结构【把所有的节点用一根直线穿起来】
连续存续[数组]
1.什么叫数组
元素类型相同,大小相等
2.数组的优缺点:
离散存储[链表]
定义:
n个结点离散分配;
彼此通过指针相连;
每个节点只有一个前驱节点,每个节点只有一个后续结点;
首节点没有前驱结点,尾节点没有后续结点。
专业术语:
首结点:第一个有效结点。
尾结点:最后一个有效结点。
头结点:第一个有效结点之前的那个结点,头结点并不存放有效数据,加头结点的目的主要是为了方便对链表的操作。
头指针:指向头结点的指针变量。
尾指针:指向尾结点的指针变量。
确定一个链表只需要一个参数:头指针。
分类:
单链表
双链表
循环链表
算法:
狭义的算法与数据的存储方式密切相关
广义的算法与数据存储的方式无关
泛型:
利用某种技术达到的效果就是:不同的存储方式,执行的操作一样的。
链表的实现:
如何表示一个链表结点
typedef struct Node
{
int data; //数据域
struct Node *pNext; //指针域 指向另一个结点的整体
}NODE,*PNODE; //NODE等价于struct Node, PNODE等价于struct Node *
链表的创建(尾插法)与遍历
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
typedef struct Node
{
int data; //数据域
struct Node *pNext; //指针域
}NODE, *PNODE;
PNODE create_list(void);
void traverse_list(PNODE pHead);
int main()
{
PNODE pHead = NULL; //等价于 struct Node * pHead = NULL
pHead = create_list(); //创建一个单链表,并将该链表的头结点赋给pHead
traverse_list(pHead);
return 0;
}
PNODE create_list(void)
{
int len; //用来存放有效节点的个数
int i;
int val; //用来临时存放用户输入的节点的值
//分配了一个不存放有效数据的头结点
PNODE pHead = (PNODE)malloc(sizeof(NODE));
if (NULL == pHead)
{
printf("分配失败,程序终止!\n");
exit(-1);
}
PNODE pTail = pHead;
pTail->pNext = NULL;
printf("请输入您要生成链表节点的个数:len = ");
scanf_s("%d", &len);
for (i = 0; i < len; ++i)
{
printf("请输入第%d个节点的值: ", i + 1);
scanf_s("%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)
{
PNODE p = pHead->pNext;
while (NULL != p)
{
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
}
注:由于我刚开始学习数据结构,对于这个尾插法不太好理解,刚开始一直没理解pTail->pNext = pNew和pTail = pNew,因为我刚开始把pTail看成一个结点,其实它是一个指向结点的指针,因为在给定义结点的时候还取了一个别名*PNODE,所以PONDE实例化的pTail是一个指向结构体的指针。下面是我对尾插法的理解,有不对的地方欢迎批评指正。
PNODE pTail = pHead;
pTail->pNext = NULL;
1.创建一个指向尾结点的指针pTail,因为此时只有一个头结点,所以此时头结点就是尾结点,然后把头结点指针域清空。
pNew->data = val;
2.给新插入的结点一个数据。
pTail->pNext = pNew;
3.把指向新结点的这个指针赋给尾结点的指针,这样pTail->pNext就指向了新插入的这个结点。
pNew->pNext = NULL;
4.把新插入的这个结点的指针域置空。
pTail = pNew;
5.让pTail指向新插入的节点,即始终让pTail指向尾结点。
判断链表是否为空
求链表长度
排序(选择排序)
//判断链表是否为空
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;
int len = length_list(pHead);
PNODE p, q;
for (i = 0, p = pHead->pNext; i < len - 1; ++i, p = p->pNext)
{
for (j = i + 1, q = p->pNext; j < len; ++j, q = q->pNext)
{
if (p->data > q->data) //类似于数组中的: a[i] > a[j]
{
t = p->data; //类似于数组中的:t = a[i]
p->data = q->data; //类似于数组中的:a[i] = a[j]
q->data = t; //类似于数组中的: a[j] = t
}
}
}
}
在链表的第pos个结点前面插入一个新结点
删除第pos个结点
//在pHead所指向链表的第pos个结点的前面插入一个新的结点
//该结点的值是val,并且pos的值是从1开始
bool insert_list(PNODE pHead, int pos, int val)
{
int i = 0;
PNODE p = pHead;
//首先找到指向第pos-1个结点的指针p
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("内存分配失败\n");
}
插入操作1
//pNew->data = val;
//PNODE q = p->pNext;
//p->pNext = pNew;
//pNew->pNext = q;
//插入操作2
pNew->data = val;
pNew->pNext = p->pNext;
p->pNext = pNew;
return true;
}
bool delete_list(PNODE pHead, int pos, int* val)
{
int i = 0;
PNODE p = pHead;
//首先找到指向第pos-1个结点的指针p
while (p->pNext != NULL && i < pos - 1)
{
p = p->pNext;
++i;
}
if (i > pos - 1 || p->pNext == NULL)
{
return false;
}
PNODE q = p->pNext;
*val = q->data;
//删除p结点后的结点
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}
以上所有操作代码
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
typedef struct Node
{
int data; //数据域
struct Node *pNext; //指针域
}NODE, *PNODE;
PNODE create_list(void);
void traverse_list(PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE pHead);
bool insert_list(PNODE, int pos, int val);
bool delete_list(PNODE pHead, int pos, int* val);
void sort_list(PNODE);
int main()
{
PNODE pHead = NULL; //等价于 struct Node * pHead = NULL
pHead = create_list(); //创建一个单链表,并将该链表的头结点赋给pHead
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, 4, 5);
//traverse_list(pHead);
//删除pos位置的结点
int val;
if (delete_list(pHead, 3, &val))
{
printf("删除成功,您删除的元素是:%d\n", val);
}
else
{
printf("删除失败\n");
}
traverse_list(pHead);
return 0;
}
PNODE create_list(void)
{
int len; //用来存放有效节点的个数
int i;
int val; //用来临时存放用户输入的节点的值
//分配了一个不存放有效数据的头结点
PNODE pHead = (PNODE)malloc(sizeof(NODE));
if (NULL == pHead)
{
printf("分配失败,程序终止!\n");
exit(-1);
}
PNODE pTail = pHead;
pTail->pNext = NULL;
printf("请输入您要生成链表节点的个数:len = ");
scanf_s("%d", &len);
for (i = 0; i < len; ++i)
{
printf("请输入第%d个节点的值: ", i + 1);
scanf_s("%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)
{
PNODE p = pHead->pNext;
while (NULL != p)
{
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
}
//判断链表是否为空
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;
int len = length_list(pHead);
PNODE p, q;
for (i = 0, p = pHead->pNext; i < len - 1; ++i, p = p->pNext)
{
for (j = i + 1, q = p->pNext; j < len; ++j, q = q->pNext)
{
if (p->data > q->data) //类似于数组中的: a[i] > a[j]
{
t = p->data; //类似于数组中的:t = a[i]
p->data = q->data; //类似于数组中的:a[i] = a[j]
q->data = t; //类似于数组中的: a[j] = t
}
}
}
}
//在pHead所指向链表的第pos个结点的前面插入一个新的结点
//该结点的值是val,并且pos的值是从1开始
bool insert_list(PNODE pHead, int pos, int val)
{
int i = 0;
PNODE p = pHead;
//首先找到指向第pos-1个结点的指针p
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("内存分配失败\n");
}
插入操作1
//pNew->data = val;
//PNODE q = p->pNext;
//p->pNext = pNew;
//pNew->pNext = q;
//插入操作2
pNew->data = val;
pNew->pNext = p->pNext;
p->pNext = pNew;
return true;
}
bool delete_list(PNODE pHead, int pos, int* val)
{
int i = 0;
PNODE p = pHead;
//首先找到指向第pos-1个结点的指针p
while (p->pNext != NULL && i < pos - 1)
{
p = p->pNext;
++i;
}
if (i > pos - 1 || p->pNext == NULL)
{
return false;
}
PNODE q = p->pNext;
*val = q->data;
//删除p结点后的结点
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}