七、链表
1.typedef的用法
示例1:
#include <stdio.h>
typedef int zhengxing;//给int取了另外一个名字zhengxing
typedef struct Student
{
int sid;
int age;
} ST;
int main(void)
{
int i = 100;//等价于zhengxing i = 100;
zhengxing j = 200;
printf(“%d”, j);
struct Student st;//等价于ST st;
struct Student * ps = &st;//等价于ST * ps
ST st2;
st2.age = 900;
printf(“%d”, st2.age);
return 0;
}
示例2:
简略结构体指针的声明
#include <stdio.h>
typedef struct Student
{
int sid;
int age;
} * PST, ST; //PST等价于 struct Student * , ST等价于struct Student
int main(void)
{
ST st;
PST pst = &st;
pst->sid = 99;
printf(“%d”, pst->sid);
return 0;
}
2.离散存储【链表】
1.定义
n个节点离散分配
彼此通过指针相连
每个节点只有一个前驱节点,也只有一个后续节点
首节点没有前驱节点,尾节点没有后续节点
专业术语:
首节点:第一个有效的节点
尾节点:最后一个有效的节点
头结点:第一个有效节点之前的那个节点
头结点没有存数据,不存在实际意义,只是为了便于增删操作
头指针:指向头节点的指针变量
尾指针:指向尾节点的指针变量
获取一个链表需要几个参数:
只需要一个参数:头指针,其他的都可以推算
链表示例
typedef struct Node
{
int data;// 数据域
struct Node * pNext;// 指针域
}NODE, *PNODE;//NODE等价于struct Node, PNODE等价于struct Node *
2.分类
单链表
双链表
每一个节点有两个指针域
循环链表
能通过任何一个节点找到其他所有的节点
非循环链表
怎么讲q指向的节点插入到q指向节点后面?
怎么删除非循环链表节点
这样子没办法吧中间那个节点内存释放
3.算法
示例:
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
typedef struct Node
{
int data;// 数据域
struct Node * pNext;// 指针域
}NODE, *PNODE;//NODE等价于struct Node, PNODE等价于struct Node *
int main(void)
{
PNODE pHead = NULL;
pHead =
create_list();//create_list()功能,创建一个非循环单链表,并将该链表头结点的地址赋给pHead
travcrse_list(pHead);//travcrse_list(pHead),用于遍历并输出每一个元素
return 0;
}
create_list方法
PNODE create_list(void)
{
int len;
int val;
PNODE pHead =
(PNODE)malloc(sizeof(NODE));//给头结点分配NODE所需的内存空间
if (NULL == pHead)
{
printf(“内存分配失败!n”);
exit(-1);
}
PNODE pTail =
pHead;//pTail是一个单纯的指针变量,这里指向pHead,所以pTail->pNext等价于pHead->pNext
pTail->pNext = NULL;
printf(“请输入您需要生成的链表节点的个数: len = “);
scanf_s(“%d”, len);
for (int 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;
//最后尾指针的下一指向为NULL
pTail = pNew;
}
return pHead;
}
先创建头指针和尾指针,头指针指向头节点
然后让尾指针指向头指针,得到头结点的信息
接着创建链表并输入数据
其中尾指针指向的头结点的pNext指向pNew
再让尾指针指向pNew节点,pNew节点的pNext指向下一节点,如此循环
最后获得一个由指针连接起来的链表
travcrse_list方法
遍历输出链表
void travcrse_list(PNODE pHead)
{
PNODE p = pHead->pNext;
while (p != NULL)
{
printf(“%d”, p->data);
p = p->pNext;
}
}
狭义的算法与数据的存数方式密切相关
广义的算法与数据的存储方式无关
泛型:
利用某种技术达到的效果就是:不同的存数方式,执行的操作时一样的
sort_list方法
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)
{
t = p->data;
p->data = q->data;
q->data = t;
}
}
}
return;
}
insert_list方法
bool insert_list(PNODE pHead, int pos, int val)
{
PNODE p = pHead->pNext;
PNODE pNew = (PNODE)malloc(sizeof(NODE));
int len = length_list(pHead);
if (NULL == pNew)
{
printf(“内存分配失败!n”);
exit(-1);
}
if (is_empty(pHead))
{
return false;
}
if (pos < 1 || pos > len)
{
return false;
}
for (int i = 1; i < pos - 1; i++)
{
p = p->pNext;
}
pNew->data = val;
//这里要先把p->pNext赋给pNew->pNext,然后再把pNew赋给p->pNext,否则会出现错误
pNew->pNext = p->pNext;
p->pNext = pNew;
return true;
}
delete_list方法,跟insert_list差不多
bool delete_list(PNODE pHead, int pos, int * pVal)
{
PNODE p = pHead->pNext;
int len = length_list(pHead);
if (is_empty(pHead))
{
return false;
}
if (pos < 1 || pos > len)
{
return false;
}
for (int i = 1; i < pos - 1; i++)
{
p = p->pNext;
}
PNODE q = p->pNext;
*pVal = q->data;
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}
4.链表的优缺点
优:
不需要考虑存储空间大小的问题
缺:
查找费时
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!