线性结构(链表)
1. typedef的用法
typedef int zhengxing;
- 上面的代码相当于为int取了一个别名;
- zhengxing完全等价于int。
因此:
typedef struct Arr
{
int *pBase;//存储数组第一个元素地址
int len;//数组所能最大容纳元素的个数
int cnt;//当前数组有效元素的个数
} A;
- 上面的代码中为结构体Arr取了一个别名A;
- A完全等价于Arr(A arr;等价于 Arr arr;)。
再看下面:
typedef struct Arr
{
int *pBase;//存储数组第一个元素地址
int len;//数组所能最大容纳元素的个数
int cnt;//当前数组有效元素的个数
}* PA;
- PA相当于结构体Arr的指针数据类型;
- 可以通过PA直接创建Arr指针变量(PA parr = &arr);
2. 链表的基本知识
2.1 链表的定义
- 多个节点离散分配;
- 彼此通过指针相连;
- 除首结点和尾节点外每个结点只有一个前驱结点和后继结点;
- 首结点只有后继节点,尾节点只有前驱节点。
2.2 专业术语
- 头节点: 第一个有效节点之前的节点,不存放数据,增加头节点的目的是为了方便对链表进行操作;
- 首节点: 第一个有效的节点;
- 尾结点: 最后一个有效的节点;
- 头指针: 指向头节点的指针变量;
- 尾指针: 指向尾结点的指针。
2.3 链表的分类
- 单链表: 每一个节点有一个指针域,指向后继节点;
- 双链表: 每一个节点有两个指针域,分别指向前驱节点和后继节点;
- 循环链表: 所有节点连接成一个环,能通过任何一个节点找到其他所有的节点;
- 非循环链表: 不成环的链表。
2.4 链表的优缺点
离散存储【链表】:
- 优点:空间没有限制,插入元素快;
- 缺点:存取速度很慢。
连续存储【数组】:
- 优点:存取速度很快;
- 缺点:空间有限,需要大量连续内存快,插入元素效率低。
3. 链表的算法
3.1 链表的结构体
链表的结构体分为数据域和指针域,数据域用来存放数据,指针域用来存放当前节点的后继节点的地址,结构体的详细代码如下:
typedef struct Node
{
int data;//数据域
Node * next;//指针域
}NODE, *PNODE;
3.2 创建链表
创建链表可以有头节点,也可以没有头节点,以下是有头节点的代码:
//创建链表
PNODE create_list(int val)
{
PNODE pHead = (PNODE)malloc(sizeof(NODE));
pHead->next = (PNODE)malloc(sizeof(NODE));
pHead->next->data = val;
//保证链表的末尾指向NULL
pHead->next->next = NULL;
return pHead;
}
3.3 创建节点
在添加数据之前需要申请数据的存储空间,所以需要创造节点.这种方式比较灵活,可以创建没有头节点的链表,也可以传NULL为参数,创建一个头节点,不仅仅可以用于创造普通节点,代码如下:
//创建节点
PNODE create_node(int val)
{
PNODE pnode = (PNODE)malloc(sizeof(NODE));
pnode->data = val;
//保证链表的末尾指向NULL
pnode->next = NULL;
return pnode;
}
3.4 遍历链表
遍历链表只需要从头开始,一个一个输出即可,当指针空指时即便利完成,代码如下:
//遍历链表
void show_list(PNODE pHead)
{
PNODE p = pHead->next;
while(p != NULL){
printf("%d\n",p->data);
p = p->next;
}
return;
}
3.5 求链表长度
求链表长度与遍历链表类似,代码如下:
//求链表长度
int length_list(PNODE pHead)
{
PNODE p = pHead->next;
int len = 0;
while(p != NULL){
len++;
p = p->next;
}
return len;
}
3.6 是否为空
只需要判断头指针后面是否为空即可,代码如下:
//是否为空
bool is_empty(PNODE pHead)
{
if(pHead->next == NULL)
return true;
return false;
}
3.7 插入
3.7.1 按照位置插入
- 按照位置插入需要找到想要插入的位置前一个节点;
- 然后通过前一个节点把待插入的节点连在后一个节点之前;
- 最后把前面的节点与待插入节点连接即可。
具体代码如下:
//按位置插入插入
bool insert_list(PNODE pHead,int pos,int val)
{
PNODE p = pHead;
//找到插入位置的前一个节点
for(int i= 1;i < pos;i++)
{
//判断是否可以插入
if(p->next != NULL)
{
p = p->next;
}
else
{
printf("链表不够长,不可以插入\n");
return false;
}
}
//创建节点并赋值
PNODE node = create_node(val);
//将节点插入指定位置
node->next = p->next;
p->next = node;
return true;
}
3.7.2 头插法
头插法就是把待插入的节点插在头节点与首节点之间,具体代码如下:
//头插法
void add_head(PNODE pHead,int val)
{
//创建节点并赋值
PNODE node = create_node(val);
//将节点插入头节点后
node->next = pHead->next;
pHead->next = node;
return;
}
3.7.3 尾插法
尾插法就是把待插入节点追加在链表末尾,代码如下:
//尾插法
void add_tail(PNODE pHead,int val)
{
//创建节点并赋值
PNODE node = create_node(val);
//查找最后一个节点
PNODE p = pHead->next;
while(p->next != NULL){
p = p->next;
}
//将节点插入指定位置
p->next = node;
return;
}
3.7.4 删除
删除一个节点只需要找到这个节点之前的节点,然后把待删除节点之前的节点与其之后的节点连接上,具体代码如下:
//删除
bool delete_list(PNODE pHead,int pos)
{
PNODE p = pHead;
//找到删除位置的前一个节点
for(int i= 1;i < pos;i++)
{
//判断是存在
if(p->next != NULL)
{
p = p->next;
}
else
{
printf("节点不存在,删除失败\n");
return false;
}
}
//删除
p->next = p->next->next;
}
3.7.5 排序(插入排序)
在数组中我们用了冒泡排序,这一次使用插入排序,插入排序从广义的角度描述就是把一个不规则序列中的元素一个一个的插入到一个新的内存空间,每一次插入都从头开始比较,然后插入在适合的位置。
狭义算法的代码如下:
//排序(插入排序从大到小排列)
void sort_list(PNODE pHead)
{
PNODE pHead2 = create_node(NULL);
//通过循化一个一个节点的插入
while(pHead->next != NULL)
{
//取节点
PNODE node = pHead->next;
pHead->next = node->next;
node->next = NULL;
//创建一个指针用来操控新链表
PNODE p = pHead2;
//插入
while(true)
{
if(p->next == NULL)
{
p->next = node;
break;
}
else if(p->next->data <= node->data)
{
node->next = p->next;
p->next = node;
break;
}
p=p->next;
}
}
pHead->next = pHead2->next;
return;
}
3.7.6 测试用的main函数
代码如下:
int main(void)
{
//创建头指针
PNODE pHead = NULL;
//创建链表并将头指针指向头节点
pHead = create_list(1);
//显示链表信息
show_list(pHead);
printf("==============\n");
//插入
insert_list(pHead,1,2);
add_head(pHead,3);
add_tail(pHead,100);
insert_list(pHead,100,2);
add_head(pHead,14);
add_tail(pHead,56);
//显示链表信息
show_list(pHead);
printf("==============\n");
//删除
delete_list(pHead,1);
//显示链表信息
show_list(pHead);
printf("==============\n");
//插入排序
sort_list(pHead);
//显示链表信息
show_list(pHead);
printf("==============\n");
//获取链表长度
int len = length_list(pHead);
printf("%d\n",len);
return 0;
}