嵌入式学习系列来到数据结构与算法部分,本篇主要讲述了数据结构与算法常用主要知识总结,用于嵌入式基础巩固,开篇首先回顾带入结构体等关键字用法,另着重从数据结构和算法两者进行知识总结,分别从线性表(顺序表、链式表)、栈、队列、树(二叉树、最优二叉树)、图、查找和排序带着示例代码一起即学即用,活学活用,巩固自己编程基础,编译环境Ubuntu。
整篇是本人通过学习进行笔记记载,包含数据结构与算法全部重要知识,将其集中在一篇,便于学习,如有不足,还请指出。另外我们提供嵌入式项目毕设资料与服务,远程部署、代码讲解、全方位服务。我们有专门的毕设项目辅导群(QQ:596557438)!!!
项目源码资料下载地址:箫声商城
易学蔚来全套毕设演示(看上哪个选那个): 易学蔚来全套毕业设计汇总 · 语雀
毕设服务真实反馈,在线观看: 客户真实反馈 · 语雀
一、知识回顾
1.typedef
typedef 原类型 新类型
增加代码的可读性;修改类型别名;
2.结构体
typedef struct 结构体{
类型 成员1;
类型 成员2;
...
类型 成员n;
}strc_t;
定义变量:
struct 结构体名 变量名;
strc_t 变量名;
定义指针:
struct 结构体名 * 变量名;
strc_t *变量名;
访问结构体成员:
变量.成员
指针->成员 《==》(*指针).成员
/*===============================================
* 文件名称:shiyan1.c
* 创 建 者:箫声.
* 创建日期:2024年03月26日
* 描 述:运用结构体编程输入一个学生的数学期中和期末成绩,计算平均值
================================================*/
#include <stdio.h>
int main(int argc, char *argv[])
{
struct student{
float mid;
float end;
float soc;
};
struct student xls;
printf("please input mid:");
scanf("%f",&xls.mid);
printf("please input end:");
scanf("%f",&xls.end);
xls.soc = (xls.mid+xls.end)/2;
printf("%.2f\n",xls.soc);
return 0;
}
输出结果:
please input mid:56
please input end:100
78.00
/*===============================================
* 文件名称:shiyan1_1.c
* 创 建 者:箫声.
* 创建日期:2022年07月08日
* 描 述:运用结构体编程输入一个学生的数学期中和期末成绩,计算平均值
================================================*/
#include <stdio.h>
#include <stdlib.h>
struct student{
float mid;
float end;
float soc;
};
int foo(float *mid,float *end,float *soc)
{
*soc = (*mid+*end)/2;
return 0;
}
int main(int argc, char *argv[])
{
struct student xls ;
printf("please input mid:");
scanf("%f",&xls.mid);
printf("please input end:");
scanf("%f",&xls.end);
struct student *p ;
p = &xls;
foo(&xls.mid,&xls.end,&xls.soc);
printf("%.2f\n",(*p).soc);
return 0;
}
输出结果:
please input mid:88
please input end:99
93.50
/*===============================================
* 文件名称:shiyan2.c
* 创 建 者: 箫声.
* 创建日期:2024年03月26日
* 描 述:union共用体
================================================*/
#include <stdio.h>
union student{
int a;
char b;
};
int main(int argc, char *argv[])
{
union student xls;
printf("%p\n",&xls.a); //取地址
printf("%p\n",&xls.b);
printf("%ld\n",sizeof(xls)); //输出共用体大小
return 0;
}
输出结果:
0x7ffe76c0f4d4
0x7ffe76c0f4d4
4
3.malloc
malloc:动态分配内存
#include <stdli.b>
void *malloc(size_t size);参数为想要分配的整数,大于0的正整数
void free(void *ptr);
注:
1)成功:返回值为分配空间首地址,失败:NULL
2)malloc分配的空间未初始化
3)size值一般为存放数据的整数倍:n*sizeof(类型)
4)malloc返回值为void*,void *可以存放任何数据,使用时最好强转成指定类型再使用
5)malloc使用完毕,需要free,free的指针必须是malloc的返回值
6)free后的指针需要指向NULL
二、数据结构与算法
1.数据结构
程序 = 数据结构 + 算法
数据结构:
1)逻辑结构(跟存储无关):
线性关系:除了头节点和尾结点之外,其他节点都只有一个直接前驱和直后继,头节点没有前驱,尾节点没有后继(表,栈,堆列)
非线性关系:指数据元素之间存在一对一、一对多或多对多的关系。这种关系不同于线性结构中的每个数据元素最多只有一个直接前驱和一个直接后继的限制。
以下是一些常见的非线性数据结构及其逻辑结构描述:
1.1 **树(Tree)**:
在树结构中,每个数据元素(节点)最多有一个前驱(父节点)和多个后继(子节点),但不存在其他的前驱或后继。这形成了一种层次关系,其中根节点没有父节点,叶节点没有子节点。
1.2 **图(Graph)**:
图是由节点(数据元素)和边(关系)组成的集合。每个节点可以有零个或多个相邻节点,而每条边连接两个节点。图可以有多种类型,包括有向图和无向图、连通图和非连通图等。
1.3 **哈希表(Hash Table)**:
虽然哈希表通常用于存储线性数据结构,但其背后的逻辑结构是基于哈希函数将键映射到数组索引上。哈希冲突的解决方式(如链地址法)会引入一种非线性关系,即同一份数据可能存储在不同的物理位置上。
1.4 **堆(Heap)**:
堆是一种特殊的完全二叉树,其中每个父节点的值都不大于或不小于其所有子节点的值(最大堆和最小堆)。尽管堆是一个树结构,但它的非线性关系体现在节点与子节点之间的这种特定的值关系上。
2)树形结构:一对多,除了根节点和叶子节点,其他节点都只有唯一前驱,多个后继节点没有前驱,叶子节点没有后继
3)图形结构:多对多,所有节点都可以有多个前驱和多个后继
4)存储结构(跟存储有关):
顺序存储结构:(类似数组)在内存中连续存储
链式存储结构:(指针)在内存中零散存储
索引存储结构:在内存中通过附加建立一个索引表,按某一个关键字递增或递减排列的逻辑次序
散列存储结构(哈希表):通过构造散列函数来确定数据存储地址或查找地址
操作:
增加、删除、修改、查询、排序........
2.算法
算法是由若干条指令所组成的有穷序列,其中每条指令表示计算机的一个或多个操作。
时间复杂度:语句的频度之和(T)(语句执行的次数)
空间复杂度:程序执行过程中所需存储空间的多少
3. 线性表
在数据结构与算法中,线性表(Linear List)是一种最简单的抽象数据类型,它是由零个或多个数据元素组成的有限序列,其中数据元素之间的关系是一对一的,即除了第一个元素外,每个元素都有且仅有一个直接前驱,除了最后一个元素外,每个元素都有且仅有一个直接后继。
线性表的特点包括:
1. 顺序存储:线性表的数据元素通常在物理位置上是连续的,以便可以通过索引快速访问任何一个元素。
2. 随机访问:线性表支持随机访问,即可以直接通过索引来访问线性表中的任何一个元素。
3. 线性关系:线性表中的数据元素之间存在线性关系,每个元素最多只有一个前驱和一个后继。
线性表的常见实现方式包括数组和链表。
数组是线性表的一种顺序存储结构,它将线性表的元素存储在连续的内存单元中,通过元素的索引来访问;
链表是线性表的一种链式存储结构,它由一系列节点组成,每个节点包含数据域和指向下一个节点的指针。
线性表是最基本的数据结构之一,许多复杂的算法和数据结构都是建立在线性表的基础之上的。例如,栈和队列这两种数据结构就可以看作是特殊的线性表。
1.顺序表
线性表:指的是逻辑结构,在逻辑上呈现线性关系,一对一关系
顺序表:指的是存储结构,在内存上连续存储,在逻辑上和存储上都是挨着的
顺序表程序应用:
struct xxx{
int data[10];
int len;// 表尾指针:相对位置关系,指向最后一个有效元素的下标
};
方便如下:取别名
优点:简单,查找方便
缺点:需要一片较大的连续空间,插入和删除需要移动空间,尤其是在头部插入删除
1.1 顺序表的基本运行实现
整型顺序表:
#ifndef _SQLIST_H_
#define _SQLIST_H_
#define SIZE 100 //定义常量SIZE为100表示存储空间总量
typedef int data_t;
typedef struct {
data_t data[SIZE]; //顺序表
int len;// 表尾指针:相对位置关系,指向最后一个有效元素的下标
}sqlist_t;
sqlist_create(); //创建一张表
sqlist_destory(); // 删除表
//增、删、改、查......
1.2 代码演示示例
1)sqlist.h
/*===============================================
* 文件名称:sqlist.h
* 创 建 者:箫声.
* 创建日期:2024年03月26日
* 描 述:顺序表基本功能实现的.h文件
================================================*/
#ifndef _SQLIST_H_
#define _SQLIST_H_
#define SIZE 100 //顺序表的大小
typedef int data_t;
typedef struct {
data_t data[SIZE]; //顺序表
int len;// 表尾指针:相对位置关系,指向最后一个有效元素的下标
}sqlist_t;
sqlist_t *sqlist_create(void);//创建表,不需要
void sqlist_destory(sqlist_t *head); // 删除表
//判断表是否为满表
int sqlist_isFull(sqlist_t *head);
//判断表是否为空表
int sqlist_isEmpty(sqlist_t *head);
//向表中插入元素,int 是返回值:在head表中pos位置插入元素data
int sqlist_insert(sqlist_t *head,int pos,data_t data);
//把pos位置元素删除
int sqlist_pos_delete(sqlist_t *head,int pos);
//删除data数据
int sqlist_data_delete(sqlist_t *head,data_t data);
//把pos位置的数据更新为data
int sqlist_pos_update(sqlist_t *head,int pos,data_t data);
//把old data数据更新为newdata
int sqlist_data_update(sqlist_t *head,data_t old_data,data_t new_data);
//查询pos位置的数据
data_t sqlist_pos_search(sqlist_t *head,int pos);
//查找data数据的位置
int sqlist_data_search(sqlist_t *head,data_t data);
//显示表的内容
void ssqlist_display(sqlist_t *head);
#endif
2)sqlist.c
/*===============================================
* 文件名称:sqlist.c
* 创 建 者:箫声.
* 创建日期:2024年03月26日
* 描 述:顺序表基本功能实现的.c文件
================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sqlist.h"
sqlist_t *sqlist_create(void)
{
sqlist_t *head = (sqlist_t*)malloc(sizeof(sqlist_t));
if (NULL == head)
{
printf("malloc failed!\n");
return NULL;
}
memset(head, 0, sizeof(sqlist_t)); //初始化head结构体为0
head->len = -1; //没有有效元素
return head;
}
void sqlist_destory(sqlist_t *head)
{
free(head);
}
int sqlist_isFull(sqlist_t *head)
{
return (SIZE-1 == head->len) ? 1 : 0;
}
int sqlist_isEmpty(sqlist_t *head)
{
return (-1 == head->len) ? 1 : 0;
}
int sqlist_insert(sqlist_t *head, int pos, data_t data)
{
//1. 判断表是否为满表,如果满表插入失败返回
if (sqlist_isFull(head))
{
printf("sqlist is full, insert failed!\n");
return -1;
}
//2. 判断插入的位置是否有效,如果无效报错返回
if ((pos < 0) || (pos > head->len+1))
{
printf("pos is invalid, insert failed!\n");
return -1;
}
//3. 把pos位置空出来
int i;
for (i = head->len+1; i > pos; i--)
head->data[i] = head->data[i-1];
//4. 把data放入pos位置
head->data[pos] = data;
//5. 表尾指针向后移动一个位置
head->len++;
return 0;
}
int sqlist_pos_delete(sqlist_t *head, int pos)
{
//1. 判断表是否为空表,空表则删除失败返回
if (sqlist_isEmpty(head))
{
printf("sqlist is empty, update failed!\n");
return -1;
}
//2. 判断pos位置是否有效,无效则报错返回
if ((pos < 0) || (pos > head->len))
{
printf("pos is invalid, update failed!\n");
return -1;
}
//3. 把pos后的元素依次往前移动一个位置,覆盖pos
int i;
for (i = pos; i < head->len; i++)
head->data[i] = head->data[i+1];
//4. 表尾指针向前移动一个位置
head->len--;
return 0;
}
int sqlist_data_delete(sqlist_t *head, data_t data)
{
//1 查找data所在位置pos,查找失败报错返回
int pos = sqlist_data_search(head, data);
if (-1 == pos)
{
printf("No such data, delete failed!\n");
return -1;
}
//2. 调用按位置删除即可
sqlist_pos_delete(head, pos);
return 0;
}
int sqlist_pos_update(sqlist_t *head, int pos, data_t data)
{
//1. 判断表是否为空表,空表则删除失败返回
if (sqlist_isEmpty(head))
{
printf("sqlist is empty, update failed!\n");
return -1;
}
//2. 判断pos位置是否有效,无效则报错返回
if ((pos < 0) || (pos > head->len))
{
printf("pos is invalid, update failed!\n");
return -1;
}
//3. 直接把data放入pos位置
head->data[pos] = data;
return 0;
}
int sqlist_data_update(sqlist_t *head, data_t old_data, data_t new_data)
{
//1 查找data所在位置pos,查找失败报错返回
int pos = sqlist_data_search(head, old_data);
if (-1 == pos)
{
printf("No such data, update failed!\n");
return -1;
}
//2. 直接把data放入pos位置
head->data[pos] = new_data;
return 0;
}
data_t sqlist_pos_search(sqlist_t *head, int pos)
{
//1. 判断表是否为空表,空表则删除失败返回
if (sqlist_isEmpty(head))
{
printf("sqlist is empty, search failed!\n");
return -1;
}
//2. 判断pos位置是否有效,无效则报错返回
if ((pos < 0) || (pos > head->len))
{
printf("pos is invalid, search failed!\n");
return -1;
}
//3. 返回pos位置的数据
return head->data[pos];
}
int sqlist_data_search(sqlist_t *head, data_t data)
{
//1. 遍历表中所有元素,遇data返回位置,找不到返回-1
int i;
for (i = 0; i < head->len+1; i++)
{
if(head->data[i]==data)
return i;
}
return -1;
}
void sqlist_display(sqlist_t *head)
{
int i;
for (i = 0; i < head->len+1; i++)
printf("%d ", head->data[i]);
printf("\n");
}
3)main.c
/*===============================================
* 文件名称:main.c
* 创 建 者:箫声.
* 创建日期:2024年03月26日
* 描 述:主函数main.c
================================================*/
#include <stdio.h>
#include "sqlist.h"
int main(int argc, char *argv[])
{
sqlist_t *head = sqlist_create();
if (NULL == head)
{
printf("create sqlist failed!\n");
return -1;
}
printf("head = %p\n", head); //头地址
int n = 12;
while (n--)
{
if (-1 == sqlist_insert(head, 0, n+1))
break;
}
sqlist_display(head); //显示插入的12个数值 1 2 3 4 5 6 7 8 9 10 11 12
printf("5's pos: %d\n", sqlist_data_search(head, 100)); //遍历表中所有元素,遇100返回位置,找不到返回-1
printf("5's data: %d\n", sqlist_pos_search(head, 5)); //判断pos位置是否有效,无效则报错返回,有效则返回该位置数值
sqlist_data_update(head, 5, 100); //更换pos位置数据
sqlist_pos_update(head, 10, 1000); //直接将数据放入pos位
sqlist_display(head); //显示数值
sqlist_pos_delete(head, 0); //删除pos位的值
sqlist_pos_delete(head, 10);
sqlist_data_delete(head, 100); //直接删除数据
sqlist_display(head);
sqlist_destory(head); //
head = NULL;
return 0;
}
4)输出结果
2 字符串顺序表
#define SIZE 100 //定义常量SIZE为100,表示存储空间总量
typedef float data_t; //可通过此方法更换类型
typedef struct {
data_t data[SIZE]; //顺序表
int len;// 表尾指针:相对位置关系,指向最后一个有效元素的下标
}sqlist_t;
sqlist L; //定义一个顺序表L
4.链式表:单链(单向,从头到尾)、双链
链表:在逻辑上线性关系,在内存储上是散列存储的
节点:[数据域][指针域](存放下一个节点的地址,如果没有下一个节点则为空)
有头链表:头节点数据域无效,
插入、删除,head不需要改变
无头链表:第一个节点为有效节点
在头部进行插入、删除,head要改变
4.1 排序
4.2 clear用法
4.3 链表删除
4.4 有头链表、无头链表
4.5 链表倒置
4.6 链表长度
4.7 插入
4.8 单向链表代码演示示例
1)linklist.h
/*===============================================
* 文件名称:linklist.h
* 创 建 者:箫声.
* 创建日期:2024年03月16日
* 描 述:单向链表.h文件
================================================*/
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
typedef int data_t;
typedef struct node{
data_t data; //数据域
struct node *next; //指针域:指向下一个节点
}node_t;
node_t *linklist_create();
void linklist_destory(node_t *head);
void linklist_clear(node_t *head);
//判断链表是否为满表
int linklist_isFull(node_t *head);
//判断链表是否为空表
int linklist_isEmpty(node_t *head);
//判断链表的长度
int linklist_length(node_t *head);
//插入链表
int linklist_insert(node_t *head,int pos ,data_t data);
int linklist_insert(node_t *head, int pos, data_t data);
int linklist_pos_delete(node_t *head, int pos);
int linklist_data_delete(node_t *head, data_t data);
int linklist_pos_update(node_t *head, int pos, data_t data);
int linklist_data_update(node_t *head, data_t old_data, data_t new_data);
//按位置pos进行查询
data_t linklist_pos_search(node_t *head, int pos);
//按数据data进行查询
node_t *linklist_data_search(node_t *head, data_t data);
//链表的倒置
void linklist_invert(node_t *head);
void linklist_sort(node_t *head);
void linklist_display(node_t *head);
#endif
2)linklist.c
/*===============================================
* 文件名称:linklist.c
* 创 建 者:箫声.
* 创建日期:2024年03月16日
* 描 述:单向链表.c文件
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linklist.h"
node_t *linklist_create(void) //创建表,不需要
{
//static sqlist_t sq; //
node_t *head = (node_t *)malloc(sizeof(node_t));
if(NULL == head)
{
printf("malloc failed!\n");
return NULL;
}
memset(head,0,sizeof(node_t)); //0表示初始化为0
//memset内存清空函数,在这的作用是用于把首地址的空间进行初始化
head->next = NULL; //最后一个有效下标,没有有效元素
return head;
}
void linklist_destory(node_t *head)
{
linklist_clear(head);
free(head);
}
void linklist_clear(node_t *head)
{
node_t *p = head->next; //第一个有效节点
node_t *q = NULL;
while (p != NULL) //判断有效节点是否存在
{
q = p->next; //先把下一个节点保存
free(p); //销毁p
p = q; //把下一个节点赋给p
}
head->next = NULL; //把头节点指针域置空
}
//判断链表是否为满表
int linklist_isFull(node_t *head)
{
return 0;
}
//判断链表是否为空表
int linklist_isEmpty(node_t *head)
{
return (NULL == head->next) ? 1 : 0;
}
//判断链表是否为空表
int linklist_length(node_t *head)
{
int len = 0;
node_t *p = head->next; //p指向第一个有效节点
while (p != NULL) //判断节点是否存在
{
len++;
p = p->next; //p向下一个节点偏移
}
return len;
}
int linklist_insert(node_t *head, int pos, data_t data)
{
//1. 判断pos是否有效,无效则报错返回
int len = linklist_length(head);
if ((pos < 0) || (pos > len))
{
printf("pos is invalid, insert failed!\n");
return -1;
}
//2. 把data包装成节点p
node_t *p = (node_t *)malloc(sizeof(node_t));
if (NULL == p)
{
printf("malloc error, insert failed!\n");
return -1;
}
p->data = data;
p->next = NULL;
//3. 找到要插入位置的前一个节点q
node_t *q = head;
while (pos--)
q = q->next;
//4. 把p插入节点q后面
p->next = q->next;
q->next = p;
return 0;
}
int linklist_pos_delete(node_t *head, int pos)
{
//1. 判断pos位置是否有效
int len = linklist_length(head);
if ((pos < 0) || (pos >= len))
{
printf("pos is invalid, delete failed!\n");
return -1;
}
//2. 找到删除节点q的前一节点p
node_t *p = head;
node_t *q = NULL;
while (pos--)
p = p->next;
q = p->next;
//3. 删除节点q
p->next = q->next;
free(q);
return 0;
}
int linklist_data_delete(node_t *head, data_t data)
{
//1. 找到删除数据data的位置pos
int pos = 0;
node_t *p = head->next;
while (p != NULL)
{
if (p->data == data)
break;
pos++;
p = p->next;
}
if (NULL == p)
{
printf("No such data, delete failed!\n");
return -1;
}
//2. 调用posDelete删除数据
linklist_pos_delete(head, pos);
return 0;
}
int linklist_pos_update(node_t *head, int pos, data_t data)
{
//1.保证pos的位置有效,无效则失败返回
int len = linklist_length(head);
if ((pos < 0) || (pos >= len))
{
printf("pos is invalid, update failed!\n");
return -1;
}
//2.返回pos位置的数据
node_t *p = head->next;
while (pos--)
p = p->next;
p->data = data;
return 0;
}
int linklist_data_update(node_t *head, data_t old_data, data_t new_data)
{
node_t *p = linklist_data_search(head, old_data);
if (NULL == p)
{
printf("No such data, update failed!\n");
return -1;
}
p->data = new_data;
return 0;
}
//按位置pos进行查询
data_t linklist_pos_search(node_t *head, int pos)
{
//1.判断Pos位置是否有效,无效则报错返回
int len = linklist_length(head);
if ((pos < 0) || (pos >= len))
{
printf("pos is invalid, search failed!\n");
return (data_t)-1;
}
//2.返回pos位置的数据
node_t *p = head->next;
while (pos--)
p = p->next;
return p->data;
}
//按数据data进行查询
node_t *linklist_data_search(node_t *head, data_t data)
{
//1.遍历
node_t *p = head->next;
while (p != NULL)
{
if (p->data == data)
return p;
p = p->next;
}
printf("No such data, search failed!\n");
return NULL;
}
void linklist_display(node_t *head)
{
node_t *p = head->next;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
void linklist_invert(node_t *head)
{
//把链表断开两个链表,将头节点存放的地址保留到p,即一个只保留头,一个只有有效节点
node_t *p = head->next;
node_t *q = NULL;
//头节点为空
head->next = NULL;
//遍历所有的有效节点,使用头插法插入到只有头的链表中
while(p != NULL)
[
//将上一个有效节点保留到q
q = p->next;
//头插法:把P插入到head后面
p->next = head->next;
head->next = p;
p = q;
}
}
void linklist_sort(node_t *head)
{
node_t *p = head->next;//第一个有效节点
node_t *q = NULL;//P的下一个节点
node_t *t = NULL;//要插入位置的前一个节点
node_t *r = NULL;//要插入位置的节点
head->next = NULL;//断开链表为两个链表
//遍历
while(p != NULL)
{
q = p->next;
t = head;
r = t->next;
while (r != NULL && p->data > r->data)
{
t = r;
r = t->next;
}
//把p插入t后面
p->next = r;
t->next = p;
//再插入下一个节点
p = q;
}
}
3)main.c
/*===============================================
* 文件名称:main.c
* 创 建 者:箫声.
* 创建日期:2024年03月16日
* 描 述:单向链表mian文件
================================================*/
#include <stdio.h>
#include "linklist.h"
int main(int argc, char *argv[])
{
node_t *head = linklist_create();
if (NULL == head)
{
printf("create linklist failed!\n");
return -1;
}
printf("head = %p\n", head);
int n = 12;
while (n--)
{
if (-1 == linklist_insert(head, 0, n+1))
break;
}
linklist_display(head);
printf("5's pos: %p\n", linklist_data_search(head, 10));
printf("5's data: %d\n", linklist_pos_search(head, 5));
linklist_data_update(head, 5, 100);
linklist_pos_update(head, 10, 1000);
linklist_display(head);
linklist_pos_delete(head, 0);
linklist_pos_delete(head, 10);
linklist_data_delete(head, 100);
linklist_display(head);
linklist_insert(head, 0, 90);
linklist_insert(head, 4, 50);
linklist_insert(head, 8, 50);
linklist_display(head);
linklist_sort(head);
linklist_display(head);
linklist_invert(head);
linklist_display(head);
linklist_destory(head);
head = NULL;
return 0;
}
4)输出结果
4.9 单向循环链表
注意事项:
1)保证最后一个节点的指针域指向头节点
2)遍历时结束标志为头节点
3)循环删除时,注意不要删除头节点
4)可以循环插入、删除等
4.10 双向循环链表
双向链表:
1.结构体建立:
typdef int data_t;
struct node{
data_t data; //数据域
struct node *prior; //前指针:用于存放前一个节点的地址
struct node *next; //后指针:用于存放后一个节点的地址
};
2.双向插入:
中间插入:
q->next = p->next;
p->next-prior = q;
p->next = q;
q->next = p;
尾部插入:
p->next = q;
q->prior = p;
q->next = NULL;
3.双向删除:
中间删除:
p->next = q->next;
q->next->prior = p;
free(q);
尾部删除:
p->next = NULL;
free(q);
1)双向链表
2)双向插入
3) 双向删除
4)代码示例
1)cyclinklist.h
/*===============================================
* 文件名称:cyclinklist.h
* 创 建 者:箫声.
* 创建日期:2024年03月27日
* 描 述:循环链表实现的.h文件
================================================*/
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
typedef int data_t;
typedef struct node{
data_t data; //数据域
struct node *next; //指针域:指向下一个节点
}node_t;
node_t *linklist_create();
void linklist_destory(node_t *head);
void linklist_clear(node_t *head);
int linklist_isFull(node_t *head);
int linklist_isEmpty(node_t *head);
int linklist_length(node_t *head);
int linklist_insert(node_t *head, int pos, data_t data);
int linklist_pos_delete(node_t *head, int pos);
int linklist_data_delete(node_t *head, data_t data);
int linklist_pos_update(node_t *head, int pos, data_t data);
int linklist_data_update(node_t *head, data_t old_data, data_t new_data);
data_t linklist_pos_search(node_t *head, int pos);
node_t *linklist_data_search(node_t *head, data_t data);
//实现链表的倒置
void linklist_invert(node_t *head);
void linklist_sort(node_t *head);
void linklist_display(node_t *head);
#endif
2)cyclinklist.c
/*===============================================
* 文件名称:cyclinklist.c
* 创 建 者:箫声.
* 创建日期:2024年03月27日
* 描 述:循环链表实现的.c文件
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linklist.h"
node_t *linklist_create()
{
node_t *head = (node_t *)malloc(sizeof(node_t));
if (NULL == head)
{
printf("malloc failed!\n");
return NULL;
}
memset(head, 0, sizeof(node_t));
head->next = head;
return head;
}
void linklist_destory(node_t *head)
{
linklist_clear(head);
free(head);
}
void linklist_clear(node_t *head)
{
node_t *p = head->next; //第一个有效节点
node_t *q = NULL;
while (p != head) //判断有效节点是否存在
{
q = p->next; //先把下一个节点保存
free(p); //销毁p
p = q; //把下一个节点赋给p
}
head->next = head; //把头节点指针域head
}
int linklist_isFull(node_t *head)
{
return 0;
}
int linklist_isEmpty(node_t *head)
{
return (head == head->next) ? 1 : 0;
}
int linklist_length(node_t *head)
{
int len = 0;
node_t *p = head->next; //p指向第一个有效节点
while (p != head) //判断节点是否head
{
len++;
p = p->next; //p向下一个节点偏移
}
return len;
}
int linklist_insert(node_t *head, int pos, data_t data)
{
//1. 判断pos是否有效,无效则报错返回
int len = linklist_length(head);
if ((pos < 0) || (pos > len))
{
printf("pos is invalid, insert failed!\n");
return -1;
}
//2. 把data包装成节点p
node_t *p = (node_t *)malloc(sizeof(node_t));
if (NULL == p)
{
printf("malloc error, insert failed!\n");
return -1;
}
p->data = data;
p->next = NULL;
//3. 找到要插入位置的前一个节点q
node_t *q = head;
while (pos--)
q = q->next;
//4. 把p插入节点q后面
p->next = q->next;
q->next = p;
return 0;
}
int linklist_pos_delete(node_t *head, int pos)
{
//1. 判断pos位置是否有效
int len = linklist_length(head);
if ((pos < 0) || (pos >= len))
{
printf("pos is invalid, delete failed!\n");
return -1;
}
//2. 找到删除节点q的前一节点p
node_t *p = head;
node_t *q = NULL;
while (pos--)
p = p->next;
q = p->next;
//3. 删除节点q
p->next = q->next;
free(q);
return 0;
}
int linklist_data_delete(node_t *head, data_t data)
{
//1. 找到删除数据data的位置pos
int pos = 0;
node_t *p = head->next;
while (p != head)
{
if (p->data == data)
break;
pos++;
p = p->next;
}
if (head == p)
{
printf("No such data, delete failed!\n");
return -1;
}
//2. 调用posDelete删除数据
linklist_pos_delete(head, pos);
return 0;
}
int linklist_pos_update(node_t *head, int pos, data_t data)
{
int len = linklist_length(head);
if ((pos < 0) || (pos >= len))
{
printf("pos is invalid, update failed!\n");
return -1;
}
node_t *p = head->next;
while (pos--)
p = p->next;
p->data = data;
return 0;
}
int linklist_data_update(node_t *head, data_t old_data, data_t new_data)
{
node_t *p = linklist_data_search(head, old_data);
if (NULL == p)
{
printf("No such data, update failed!\n");
return -1;
}
p->data = new_data;
return 0;
}
data_t linklist_pos_search(node_t *head, int pos)
{
int len = linklist_length(head);
if ((pos < 0) || (pos >= len))
{
printf("pos is invalid, search failed!\n");
return (data_t)-1;
}
node_t *p = head->next;
while (pos--)
p = p->next;
return p->data;
}
node_t *linklist_data_search(node_t *head, data_t data)
{
node_t *p = head->next;
while (p != head)
{
if (p->data == data)
return p;
p = p->next;
}
printf("No such data, search failed!\n");
return NULL;
}
void linklist_display(node_t *head)
{
node_t *p = head->next;
while (p != head)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
void linklist_invert(node_t *head)
{
node_t *p = head->next;
node_t *q = NULL;
//把链表断开成两个链表,一个只有头,一个只有有效节点
head->next = head;
//遍历所有的有效节点,使用头插法插入到只有头的链表中
while (p != head)
{
q = p->next;
//头插法:把p插入到head后面
p->next = head->next;
head->next = p;
p = q;
}
}
void linklist_sort(node_t *head)
{
node_t *p = head->next; //第一个有效节点
node_t *q = NULL; //p的下一个节点
node_t *t = NULL; //要插入位置的前一个节点
node_t *r = NULL; //要插入位置的节点
head->next = head; //断开链表为两个链表
//遍历无序链表,把每一个节点在有序链表中找到相应位置插入
while (p != head)
{
q = p->next; //先保存p的下一个节点
//从head开始找到有序链表中要插入位置的前一节点t
t = head;
r = t->next;
//当插入位置r存在且p值大于r值时,则t,r都后移一个节点
while (r != head && p->data > r->data)
{
t = r;
r = t->next;
}
//把p插入t后面
p->next = r;
t->next = p;
//再插入下一个节点
p = q;
}
}
3)main.c
/*===============================================
* 文件名称:main.c
* 创 建 者:箫声.
* 创建日期:2024年03月27日
* 描 述:循环链表实现的主函数文件
================================================*/
#include <stdio.h>
#include "linklist.h"
int main(int argc, char *argv[])
{
node_t *head = linklist_create();
if (NULL == head)
{
printf("create linklist failed!\n");
return -1;
}
printf("head = %p\n", head);
int n = 12;
while (n--)
{
if (-1 == linklist_insert(head, 0, n+1))
break;
}
linklist_display(head);
printf("5's pos: %p\n", linklist_data_search(head, 10));
printf("5's data: %d\n", linklist_pos_search(head, 5));
linklist_data_update(head, 5, 100);
linklist_pos_update(head, 3, 1000);
linklist_display(head);
linklist_pos_delete(head, 0);
linklist_pos_delete(head, 10);
linklist_data_delete(head, 100);
linklist_display(head);
linklist_insert(head, 0, 90);
linklist_insert(head, 4, 50);
linklist_insert(head, 8, 50);
linklist_display(head);
linklist_sort(head);
linklist_display(head);
linklist_invert(head);
linklist_display(head);
linklist_destory(head);
head = NULL;
return 0;
}
4)输出结果
5. 线性结构:栈
栈:只能在一端进行插入或删除
栈的特点:先进后出
顺序栈:栈中数据连续存储
链式栈:栈中数据散列存储
先进后出:入栈和出栈只能在同一端进行
栈顶:进行插入删除操作的一端
栈底:栈顶的另一端就是栈底
应用:
·数制转换
十进制N转k进制,循环(执行N=N/k操作,余数入栈),直到余数为0,出栈即结果。
·表达式求值
1. 中缀表达式(Infix Notation)
运算符放在两个操作数之间。(存在优先级问题,处理速度慢)
2. 前缀表达式(Prefix Notation)
运算符放在两个操作数之前。(不存在优先级问题,自右向左扫描)
3. 后缀表达式(Postfix Notation)
运算符放在两个操作数之后。(不存在优先级问题,自左向右扫描)
·中缀表达式转后缀表达式:(1)读人操作数,直接输出到后缀表达式。
(2)读入运算符。压入运算符号栈。
①若后进的运算符优先级高于先进的,则继续进栈。
②若后进的运算符优先级不高于先进的,则将运算符号栈内高于或等于后进运算符级别的运算符依次弹出并输出到后缀表达式。
(3)括号处理。
①遇到开括号“(”,进运算符号栈。
②遇到闭括号“)”,则把最靠近的开括号“(”以及其后进栈的运算符依次弹出并输出到后缀表达式(开括号和闭括号均不输出)。
(4)遇到结束符“#”,则把运算符号栈内的所有运算符号依次弹出,开输出到后缀表达式。
(5)若输入为+、-单目运算符,改为0和运算对象在前,运算符在后。·后缀表达式求值
·中断处理和现场保护
1.顺序栈的示例代码
1)sqstack.h
/*===============================================
* 文件名称:sqstack.h
* 创 建 者:箫声.
* 创建日期:2024年03月27日
* 描 述:顺序栈功能实现的.h文件
================================================*/
#ifndef __STACK_H__
#define __STACK_H__
#define SIZE 100
typedef int data_t;
typedef struct {
data_t data[SIZE];
int top; //栈顶指针,最后一个有效元素下标
}stack_t;
stack_t *stack_create();
void stack_destory(stack_t *head);
void stack_clear(stack_t *head);
int stack_isFull(stack_t *head);
int stack_isEmpty(stack_t *head);
//在栈顶入栈
int stack_push(stack_t *head, data_t data);
//在栈顶出栈
data_t stack_pop(stack_t *head);
//获取栈顶元素
data_t stack_get_top(stack_t *head);
void stack_display(stack_t *head);
#endif
2)sqstack.c
/*===============================================
* 文件名称:sqstack.c
* 创 建 者:箫声.
* 创建日期:2024年03月27日
* 描 述:顺序栈功能实现的.c文件
================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "stack.h"
stack_t *stack_create()
{
stack_t *head = (stack_t *)malloc(sizeof(stack_t));
if (NULL == head)
{
printf("malloc error, create failed!\n");
return NULL;
}
memset(head, 0, sizeof(stack_t));
head->top = -1;
return head;
}
void stack_destory(stack_t *head)
{
free(head);
}
void stack_clear(stack_t *head)
{
head->top = -1;
}
int stack_isFull(stack_t *head)
{
return (SIZE-1 == head->top) ? 1 : 0;
}
int stack_isEmpty(stack_t *head)
{
return (-1 == head->top) ? 1: 0;
}
int stack_push(stack_t *head, data_t data)
{
if (stack_isFull(head))
{
printf("stack is full, push failed!\n");
return -1;
}
head->data[head->top+1] = data;
head->top++;
return 0;
}
data_t stack_pop(stack_t *head)
{
if (stack_isEmpty(head))
{
printf("stack is empty, pop failed!\n");
return (data_t)-1;
}
data_t data = head->data[head->top];
head->top--;
return data;
}
data_t stack_get_top(stack_t *head)
{
if (stack_isEmpty(head))
{
printf("stack is empty, no top data!\n");
return (data_t)-1;
}
return head->data[head->top];
}
void stack_display(stack_t *head)
{
int i;
for (i = 0; i <= head->top; i++)
printf("%d ", head->data[i]);
printf("\n");
}
3)main.c
/*===============================================
* 文件名称:main.c
* 创 建 者:箫声.
* 创建日期:2024年03月27日
* 描 述:顺序栈功能实现的主函数文件
================================================*/
#include <stdio.h>
#include "stack.h"
int main(int argc, char *argv[])
{
stack_t *head = stack_create();
if (NULL == head)
{
printf("create failed!\n");
return -1;
}
int n = 10;
while (n--)
stack_push(head, n+1);
stack_display(head);
n = 5;
while (n--)
{
printf("top data: %d\n", stack_get_top(head));
printf("pop data: %d\n", stack_pop(head));
}
stack_display(head);
for (n = 10; n < 15; n++)
stack_push(head, n+1);
stack_display(head);
stack_destory(head);
head = NULL;
return 0;
}
4)输出结果
2. 链式栈的示例代码
1)linkstack.h
/*===============================================
* 文件名称:linkstack.h
* 创 建 者:箫声.
* 创建日期:2024年03月27日
* 描 述:链式栈功能实现的.h文件
================================================*/
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
typedef int data_t;
typedef struct node{
data_t data; //数据域
struct node *next; //指针域:指向下一个节点
}node_t;
node_t *stack_create();
void stack_destory(node_t *head);
void stack_clear(node_t *head);
int stack_isFull(node_t *head);
int stack_isEmpty(node_t *head);
int stack_length(node_t *head);
int stack_push(node_t *head, data_t data);
data_t stack_pop(node_t *head);
data_t stack_get_top(node_t *head);
void stack_display(node_t *head);
#endif
2)linkstack.c
/*===============================================
* 文件名称:linkstack.c
* 创 建 者:箫声.
* 创建日期:2024年03月27日
* 描 述:链式栈功能实现的.c文件
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stack.h"
node_t *stack_create()
{
node_t *head = (node_t *)malloc(sizeof(node_t));
if (NULL == head)
{
printf("malloc failed!\n");
return NULL;
}
memset(head, 0, sizeof(node_t));
head->next = NULL;
return head;
}
void stack_destory(node_t *head)
{
stack_clear(head);
free(head);
}
void stack_clear(node_t *head)
{
node_t *p = head->next; //第一个有效节点
node_t *q = NULL;
while (p != NULL) //判断有效节点是否存在
{
q = p->next; //先把下一个节点保存
free(p); //销毁p
p = q; //把下一个节点赋给p
}
head->next = NULL; //把头节点指针域置空
}
int stack_isFull(node_t *head)
{
return 0;
}
int stack_isEmpty(node_t *head)
{
return (NULL == head->next) ? 1 : 0;
}
int stack_length(node_t *head)
{
int len = 0;
node_t *p = head->next; //p指向第一个有效节点
while (p != NULL) //判断节点是否存在
{
len++;
p = p->next; //p向下一个节点偏移
}
return len;
}
int stack_push(node_t *head, data_t data)
{
//把data包装成节点p
node_t *p = (node_t *)malloc(sizeof(node_t));
if (NULL == p)
{
printf("malloc error, insert failed!\n");
return -1;
}
p->data = data;
p->next = NULL;
node_t *q = head;
//4. 把p插入节点q后面
p->next = q->next;
q->next = p;
return 0;
}
data_t stack_pop(node_t *head)
{
if (stack_isEmpty(head))
{
printf("stack is empty, pop failed!\n");
return (data_t)-1;
}
//2. 找到删除节点q的前一节点p
node_t *p = head;
node_t *q = p->next;
data_t data = q->data;
//3. 删除节点q
p->next = q->next;
free(q);
return data;
}
data_t stack_get_top(node_t *head)
{
if (stack_isEmpty(head))
{
printf("stack is empty, no top data!\n");
return (data_t)-1;
}
return head->next->data;
}
void stack_display(node_t *head)
{
node_t *p = head->next;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
3)main.c
/*===============================================
* 文件名称:main.c
* 创 建 者:箫声.
* 创建日期:2024年03月27日
* 描 述:链式栈功能实现的主函数文件
================================================*/
#include <stdio.h>
#include "stack.h"
int main(int argc, char *argv[])
{
node_t *head = stack_create();
if (NULL == head)
{
printf("create failed!\n");
return -1;
}
int n = 10;
while (n--)
stack_push(head, n+1);
stack_display(head);
n = 5;
while (n--)
{
printf("top data: %d\n", stack_get_top(head));
printf("pop data: %d\n", stack_pop(head));
}
stack_display(head);
for (n = 10; n < 15; n++)
stack_push(head, n+1);
stack_display(head);
stack_destory(head);
head = NULL;
return 0;
}
4)输出结果
6.线性结构:队列
特点:先进先出
先进先出:在一端插入,另一端删除
队头:可以删除的一端
队尾:插入的一端
1、顺序队列
1. 顺序队列
“假溢出”现象。
2. 循环队列
解决“假溢出”。
2、链队列
应用:
·输入输出管理
·对CPU的分配管理
·优先队列(priority queue)
权值优先。
·双(双端)队列(double-ends queue)
操作系统的调度工作则是采用双端队列。
入队:
先判断队是否满
head->data[head->rear %SIZE] =data;
head->rear++;
return 0;
int queue_en(queue_t *queue, data_t data)
{
//1. 把data包装成节点p
node_t *p = (node_t *)malloc(sizeof(node_t));
if (NULL == p)
{
printf("malloc error, entry failed!\n");
return -1;
}
p->data = data;
p->next = NULL;
//2. 直接把p插入到队尾rear后面
queue->rear->next = p;
//3. 队尾rear指向p
queue->rear = p;
return 0;
}
出队:
先判断队是否空
data_t data = head->data[head->front %SIZE];
head->front++;
return data;
data_t queue_de(queue_t *queue)
{
//1. 判断队列是否为空队,空队则报错返回
if (queue_isEmpty(queue))
{
printf("queue is empty, delete failed!\n");
return (data_t)-1;
}
//2. 删除front后的第一个有效节点p
node_t *head = queue->front; //头节点
node_t *p = head->next; //要删除的第一个有效节点
//删除head的下一个节点p
head->next = p->next;
data_t data = p->data;
free(p);
//3. 判断删除的是否是最后一个有效节点,如果是则rear指向头节点
if (NULL == head->next)
queue->rear = head;
return data;
}
1.顺序队列的示例代码
1)sqqueue.h
/*===============================================
* 文件名称:sqqueue.h
* 创 建 者:箫声.
* 创建日期:2024年03月15日
* 描 述:顺序队列的.h文件
================================================*/
#ifndef __QUEUE_H__
#define __QUEUE_H__
#define SIZE 7
typedef int data_t;
typedef struct {
data_t data[SIZE];
int front;
int rear;
}queue_t;
queue_t *queue_create();
void queue_destory(queue_t *head);
void queue_clear(queue_t *head);
int queue_isFull(queue_t *head);
int queue_isEmpty(queue_t *head);
//入队
int queue_en(queue_t *head, data_t data);
//出队
data_t queue_de(queue_t *head);
void queue_display(queue_t *head);
#endif
2)sqqueue.c
/*===============================================
* 文件名称:sqqueue.c
* 创 建 者:箫声.
* 创建日期:2024年03月15日
* 描 述:顺序队列的.c文件
================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "queue.h"
queue_t *queue_create()
{
queue_t *head = (queue_t *)malloc(sizeof(queue_t));
if (NULL == head)
{
printf("malloc failed!\n");
return NULL;
}
memset(head, 0, sizeof(queue_t));
head->front = 0;
head->rear = 0;
return head;
}
void queue_destory(queue_t *head)
{
free(head);
}
void queue_clear(queue_t *head)
{
head->front = head->rear = 0;
}
int queue_isFull(queue_t *head)
{
return (SIZE == head->rear - head->front) ? 1 : 0;
}
int queue_isEmpty(queue_t *head)
{
return (head->rear == head->front) ? 1 : 0;
}
//入队
int queue_en(queue_t *head, data_t data)
{
if (queue_isFull(head))
{
printf("queue is full, enter failed!\n");
return -1;
}
head->data[head->rear % SIZE] = data;
head->rear++;
return 0;
}
//出队
data_t queue_de(queue_t *head)
{
if (queue_isEmpty(head))
{
printf("queue is empty, delete failed!\n");
return (data_t)-1;
}
data_t data = head->data[head->front % SIZE];
head->front++;
//当出队到SIZE时,rear和front重新回到数组下标对应位置
if (SIZE == head->front)
{
head->front = 0;
head->rear = head->rear % SIZE;
}
return data;
}
void queue_display(queue_t *head)
{
int i;
for (i = head->front; i < head->rear; i++)
printf("%d ", head->data[i % SIZE]);
printf("\n");
}
3)main.c
/*===============================================
* 文件名称:main.c
* 创 建 者:箫声.
* 创建日期:2024年03月15日
* 描 述:顺序队列的主函数文件
================================================*/
#include <stdio.h>
#include "queue.h"
int main(int argc, char *argv[])
{
queue_t *head = queue_create();
if (NULL == head)
{
printf("create failed!\n");
return -1;
}
int i, n;
for (i = 1; i <= 3; i++)
queue_en(head, i);
queue_display(head);
n = 3;
while (n--)
printf("de: %d\n", queue_de(head));
queue_display(head);
for (i = 1; i <= 8; i++)
queue_en(head, i);
queue_display(head);
queue_destory(head);
head = NULL;
return 0;
}
4)输出结果
2. 链表队列的示例代码
1)链式队列删除
2)qlinkqueue.h
/*===============================================
* 文件名称:qlinkqueue.h
* 创 建 者:箫声.
* 创建日期:2024年03月15日
* 描 述:链式队列的.h文件
================================================*/
#ifndef __QUEUE_H__
#define __QUEUE_H__
typedef int data_t;
typedef struct node{
data_t data;
struct node *next;
}node_t; //节点类型
typedef struct {
node_t *front; //头节点的地址
node_t *rear; //尾节点的地址
}queue_t;
queue_t *queue_create();
void queue_destory(queue_t *queue);
void queue_clear(queue_t *queue);
int queue_isFull(queue_t *queue);
int queue_isEmpty(queue_t *queue);
//入队
int queue_en(queue_t *queue, data_t data);
//出队
data_t queue_de(queue_t *queue);
void queue_display(queue_t *queue);
#endif
3)qlinkqueue.c
/*===============================================
* 文件名称:qlinkqueue.c
* 创 建 者:箫声.
* 创建日期:2024年03月15日
* 描 述:链式队列的.c文件
================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "queue.h"
queue_t *queue_create()
{
//创建队列queue
queue_t *queue = (queue_t *)malloc(sizeof(queue_t));
if (NULL == queue)
{
printf("malloc failed!\n");
return NULL;
}
//创建头节点
node_t *head = (node_t *)malloc(sizeof(node_t));
if (NULL == head)
{
free(queue);
printf("malloc failed!\n");
return NULL;
}
head->next = NULL;
//空队,让队头和队尾指向head
queue->front = head;
queue->rear = head;
return queue;
}
void queue_destory(queue_t *queue)
{
queue_clear(queue);
free(queue->front);
free(queue);
}
void queue_clear(queue_t *queue)
{
//找到第一个有效节点
node_t *p = queue->front->next;
node_t *q = NULL;
//遍历所有的有效节点并free
while (p != NULL)
{
q = p->next;
free(p);
p = q;
}
//把头节点指针域设为NULL
queue->front->next = NULL;
//把尾节点指向头节点
queue->rear = queue->front;
}
int queue_isFull(queue_t *queue)
{
return 0;
}
int queue_isEmpty(queue_t *queue)
{
return (queue->front == queue->rear) ? 1 : 0;
}
//入队
int queue_en(queue_t *queue, data_t data)
{
//1. 把data包装成节点p
node_t *p = (node_t *)malloc(sizeof(node_t));
if (NULL == p)
{
printf("malloc error, entry failed!\n");
return -1;
}
p->data = data;
p->next = NULL;
//2. 直接把p插入到队尾rear后面
queue->rear->next = p;
//3. 队尾rear指向p
queue->rear = p;
return 0;
}
//出队
data_t queue_de(queue_t *queue)
{
//1. 判断队列是否为空队,空队则报错返回
if (queue_isEmpty(queue))
{
printf("queue is empty, delete failed!\n");
return (data_t)-1;
}
//2. 删除front后的第一个有效节点p
node_t *head = queue->front; //头节点
node_t *p = head->next; //要删除的第一个有效节点
//删除head的下一个节点p
head->next = p->next;
data_t data = p->data;
free(p);
//3. 判断删除的是否是最后一个有效节点,如果是则rear指向头节点
if (NULL == head->next)
queue->rear = head;
return data;
}
void queue_display(queue_t *queue)
{
node_t *p = queue->front->next;
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
4)main.c
/*===============================================
* 文件名称:mian.c
* 创 建 者:箫声.
* 创建日期:2024年03月15日
* 描 述:链式队列的主函数文件
================================================*/
#include <stdio.h>
#include "queue.h"
int main(int argc, char *argv[])
{
queue_t *queue = queue_create();
if (NULL == queue)
{
printf("create failed!\n");
return -1;
}
int i, n;
for (i = 1; i <= 3; i++)
queue_en(queue, i);
queue_display(queue);
n = 3;
while (n--)
printf("de: %d\n", queue_de(queue));
queue_display(queue);
for (i = 1; i <= 8; i++)
queue_en(queue, i);
queue_display(queue);
queue_destory(queue);
queue = NULL;
return 0;
}
5)输出结果
7.非线性数据结构 :树、图
1.树
术语:
结点(包含数据和分支)、结点的度(结点的子树数)、树的度(树中各结点度的最大值)、叶子(度为零)、分支结点(度不为零)、兄弟结点、层数、树的深度(高度)、森林(零或者有限棵互不相交的树的集合)、有序树(结点的子树从左到右有序)和无序树。
树根(根节点),有且仅有一个,无前驱结点。
表示法嵌套集合法(文氏图法)
圆括号表示法
凹入法
2.二叉树
2.1 性质
性质:
1)在二叉树的第i层上至多有2^(i-1)个结点;
2)深度为K(k>=1)的二叉树最多有2^K-1个节点。
3)在任意一颗二叉树中,树叶的数目(n0)比度数为2的节点(n2)的数目多一。
总节点数为各类节点之和:n=n0+n1+n2;
总节点数为所有子节点数加一:n=n1+2*n2+1
故得:n0 = n2+1;
三个结点所构成的二叉树有五种形式。
完全二叉树深度为h,有n个结点的二叉树。当且仅当每一个结点都与深度为h的满二叉树中编号从1至n的结点一一对应时,缺失部分一定是右边的。只有最后一层美满,且集中在左边,具有n个节点的完全二叉树的深度为 (log2n)+1。
2^k-1 = n
2^k = n+1
k = log2(n+1)
满二叉树每一层都是满的,深度为K(K>=1)时有2^(k)-1个节点的二叉树
2.2 二叉树的存储
二叉树的存储:
顺序存储结构:完全二叉树节点的编号方法是从上到下,从左到右,根节点为1号节点。
设完全二叉树的节点数为n,某节点编号为i
1)当i>1(不是根节点)时,有父节点,其编号为i/2;
2)当2*i<=n时,有左孩子,其编号为2*i,否则没有左孩子本身是叶节点;
3)当2*i+1<=n时,有右孩子,其编号为2*i+1,否则没有右孩子;
4)当i为奇数且不为1时,有左兄弟,其编号为i-1,否则没有左兄弟;
5)当i为偶数且不为n时,有右兄弟,其编号为i+1,否则没有右兄弟;
1)顺序存储结构
2)链式存储结构
2.3 二叉树的遍历
1、先序遍历二叉树(根、左、右)
先访问树根,再访问左子树,最后访问右子树;
void tree_DLR(tree_t *root)
{
if (NULL == root)
return;
printf("%d ", root->data);
tree_DLR(root->lchild);
tree_DLR(root->rchild);
}
2、中序遍历二叉树(左、根、右)
先访问左子树,再访问树根,最后访问右子树;
void tree_LDR(tree_t *root)
{
if (NULL == root)
return;
tree_LDR(root->lchild);
printf("%d ", root->data);
tree_LDR(root->rchild);
}
3、后序遍历二叉树(左、右、根)
先访问左子树,再访问右子树,最后访问树根;
void tree_LRD(tree_t *root)
{
if (NULL == root)
return;
tree_LRD(root->lchild);
tree_LRD(root->rchild);
printf("%d ", root->data);
}
4、层次遍历二叉树
对某一层的结点访问完后,在按照他们的访问次序对各个结点的左右孩子顺序访问,一层一层进行,先访问的结点其左、右孩子也要先访问
void tree_leave_show(tree_t *root)
{
//1、当树根为空时,报错返回
if(NULL == root)
{
printf("this is NULL tree!\n");
return ;
}
//2、定义队列
tree_t *queue[100] = {NULL};
int front,rear;
front = rear = 0;
//3、让根节点入队
queue[rear++] = root;
//4、循环判断队列是否为空,为空时跳出循环,否则让第一个元素出队,出队后判断其左右孩子是否存在,如果存在则让左右孩子入队
while(rear != front)
{
tree_t *p = queue[front++];
printf("%d ",p->data):
if(NULL != p->lchild);
queue[rear++] = p-lchild;
if(NULL != p->rchild);
queue[rear++] = p-rchild;
}
printf("\n");
}
}
1)tree.h
/*===============================================
* 文件名称:tree.h
* 创 建 者:箫声.
* 创建日期:2024年03月28日
* 描 述:二叉树先序遍历、中序遍历、后序遍历、层次遍历的.h文件
================================================*/
#ifndef __TREE_H__
#define __TREE_H__
typedef int data_t;
typedef struct node{
data_t data;
struct node *lchild;
struct node *rchild;
}tree_t;
//创建完全二叉树,树根编号i=1,总共节点数
tree_t *tree_create(int i, int n);
void tree_DLR(tree_t *root); //先序遍历
void tree_LDR(tree_t *root); //中序遍历
void tree_LRD(tree_t *root); //后序遍历
void tree_level_show(tree_t *root); //层次遍历
#endif
2)tree.c
/*===============================================
* 文件名称:tree.c
* 创 建 者:箫声.
* 创建日期:2024年03月28日
* 描 述:二叉树先序遍历、中序遍历、后序遍历、层次遍历的.c文件
================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "tree.h"
tree_t *tree_create(int i, int n)
{
tree_t *root = (tree_t *)malloc(sizeof(tree_t));
if (NULL == root)
{
printf("malloc failed!\n");
return NULL;
}
root->data = i;
if (2*i <= n)
root->lchild = tree_create(2*i, n);
else
root->lchild = NULL;
if (2*i+1 <= n)
root->rchild = tree_create(2*i+1, n);
else
root->rchild = NULL;
return root;
}
void tree_DLR(tree_t *root)
{
if (NULL == root)
return;
printf("%d ", root->data);
tree_DLR(root->lchild);
tree_DLR(root->rchild);
}
void tree_LDR(tree_t *root)
{
if (NULL == root)
return;
tree_LDR(root->lchild);
printf("%d ", root->data);
tree_LDR(root->rchild);
}
void tree_LRD(tree_t *root)
{
if (NULL == root)
return;
tree_LRD(root->lchild);
tree_LRD(root->rchild);
printf("%d ", root->data);
}
void tree_level_show(tree_t *root)
{
//1. 当树根为空时,报错返回
if (NULL == root)
{
printf("This is NULL tree!\n");
return;
}
//2. 定义队列
tree_t *queue[100] = {NULL};
int front, rear;
front = rear = 0;
//3. 让根节点入队
queue[rear++] = root;
//4. 循环判断队列是否为空,为空时跳出循环,否则让第一个元素出队,出队后判断其左右孩子是否存在,如果存在则让左右孩子入队
while (rear != front)
{
tree_t *p = queue[front++];
printf("%d ", p->data);
if (NULL != p->lchild)
queue[rear++] = p->lchild;
if (NULL != p->rchild)
queue[rear++] = p->rchild;
}
printf("\n");
}
3)main.c
/*===============================================
* 文件名称:main.c
* 创 建 者:箫声.
* 创建日期:2024年03月28日
* 描 述:二叉树先序遍历、中序遍历、后序遍历、层次遍历的主函数文件
================================================*/
#include <stdio.h>
#include "tree.h"
int main(int argc, char *argv[])
{
tree_t *root = tree_create(1, 12);
if (NULL == root)
{
printf("create failed!\n");
return -1;
}
printf("Preorder traversal=");
tree_DLR(root);
printf("\n");
printf("Inorder traversal=");
tree_LDR(root);
printf("\n");
printf("Postorder traversal=");
tree_LRD(root);
printf("\n");
printf("Hierarchical traversal=");
tree_level_show(root);
return 0;
}
4)输出结果
2.4 恢复二叉树
前序+中序(前序确定根节点,中序确定左子树和右子树)
根据前序序列确定根节点,根据中序序列确定左子树和右子树。
分别找出左子树和右子树的根节点,并把左、右子树的根节点连到父节点上。
对左子树和右子树重复以上两步,直到子树只剩下1个结点或2个结点或空为止。
中序+后序(后序确定根节点,中序确定左子树和右子树)同上
2.5 线索二叉树
带有线索(指向直接前驱结点或指向直接后继结点的指针)的二叉树。
优点:
·进行中序遍历时不必采用堆栈处理,遍历速度快,节约存储空间。
·任意一个结点能直接找到它相应遍历顺序的直接前驱和直接后继结点。
缺点:
·节点的插入和删除麻烦且速度慢。
·线索树不能共用。
线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程。
先序线索二叉树、中序线索二叉树(中序线索化使用最多)、后序线索二叉树。
2.6 二叉树的转换
一般树转换为二叉树
把一般树的长子作为其父结点的左子树,次弟作为其兄结点的右子树。方法:
连线:链接树中所有相邻的亲兄弟之间连线。
删线:保留父结点与长子的连线,打断父结点与非长子之间的连线。
旋转:以根节点为轴心,将整棵树顺时针旋转一定的角度,使之层次分明。
森林转换为二叉树森林是若干棵树的集合。只要将森林中的每一棵树的根视为兄弟,而每一棵树又可以用二叉树表示,这样,森林也就可以用二叉树来表示了。
方法:
(1)将森林中的每一棵树转换成相应的二叉树。
(2)第一棵二叉树保持不动,从第二棵二叉树开始,依次把后一棵二叉树的根结
点作为前一棵二叉树根结点的右子树,直到把最后一棵二叉树的根结点作为其前一棵二叉树的右子树为止。二叉树转换为树和森林
树转换为二叉树以后,其根结点必定无右子树;而森林转换为二叉树以后,其根结点有右分支。显然这一转换过程是可逆的,即可以依据二叉树的根结点有无右子树,将一棵二叉树还原为树或森林。
方法:
(1)若某结点是其父结点的左孩子,则把该结点的右孩子、右孩子的右孩子,直到最后一个石核于都与该结点的父结点连起来。
(2)删除原二叉树中所有的父结点与右孩子结点的连线。
(3)整理(1)、(2)的结果,使之层次分明。
3.最优二叉树(哈夫曼树)
术语:
路径长度(结点间)、树的路径长度(根结点到每个结点的路径长度之和)、结点的带权路径长度(结点到根结点之间路径长度与该结点上的权的乘积)、树的带权路径长度(树中所有叶子结点的带权路径长度之和)。
注:决策判定问题中,哈夫曼树可以获得最佳的决策算法。
基本思想(1)由给定的n个权值{W1,W2,…,Wn}构造n棵只有一个叶结点的二叉树,从而得到一个二叉树的集合F={T1,T2,…, Tn}。
(2)在F中选取根结点的权值最小和次小的两棵二叉树作为左、右子树构造一棵新的二叉树,这棵新的二叉树根结点的权值为其左、右子树根结点权值之和。
(3)在集合F中删除作为左、右子树的两棵二叉树,并将新建立的二叉树加入到集合F中。(4)重复(2)、(3)两步,直到F中只剩下一棵二叉树时,这棵二叉树便是所要建立的哈夫曼树。
哈夫曼编码
在数据通信中,经常需要将传送的文字转换成由二进制字符0和1组成的二进制代码,称之为编码。如果在编码时考虑字符出现的频率,让出现频率高的字符采用尽可能短的编码,出现频率低的字符采用稍长的编码,构造一种不等长编码,则电文的代码就可能更短。
哈夫曼编码是一种用于构造使电文的编码总长最短的编码方案。
1)HFMtree.c
/*===============================================
* 文件名称:HFMtree.c
* 创 建 者:箫声.
* 创建日期:2024年03月28日
* 描 述:哈夫曼树文件
================================================*/
#include <stdio.h>
#define MAXLEN 100
typedef struct //定义结构体
{
int weight; //定义一个整型权值变量
int lchild, rchild, parent; //定义左、右孩子及双亲指针
}HTNode;
typedef HTNode HT[MAXLEN]; //向量类型
int n;
void InitHFMT(HT T)
{
//哈夫曼树初始化子函数
int i;
printf("\n请输入共有多少个权值(小于100):");
scanf("%d", &n);
for (i = 0; i < 2 * n - 1;i++)
{
T[i].weight = 0;
T[i].lchild = -1;
T[i].rchild = -1;
T[i].parent = -1;
}
}
void InputWeiget(HT T)
{
//输入权值子函数
int w, i;
for (i = 0; i < n; i++)
{
printf("输入第%d个权值:", i + 1);
scanf("%d", &w);
getchar();
T[i].weight = w;
}
}
void SelectMin(HT T, int i, int *p1, int *p2)
{
//选择两个结点中小的结点
long min1 = 888888, min2 = 888888; //预设两个值,并使它大于可能出现的最大权值
int j;
for (j = 0; j <= i; j++)
{
if (T[j].parent == -1)
{
if (min1 > T[j].weight)
{
min1 = T[j].weight; //找出最小的权值
*p1 = j; //通过*p1带回序号
}
}
}
for (j = 0; j <= i; j++)
{
if (T[j].parent == -1)
{
if (min2 > T[j].weight&&j!=(*p1))
{
min2 = T[j].weight; //找出次最小的权值
*p2 = j; //通过*p2带回序号
}
}
}
}
void CreatHFMT(HT T)
{
//构造哈夫曼树,T[2*n-1]为其根结点
int i, p1, p2;
InitHFMT(T);
InputWeiget(T);
for (i = n; i < 2 * n - 1; i++)
{
SelectMin(T, i - 1, &p1, &p2);
T[p1].parent = T[p2].parent = i;
T[i].lchild = T[p1].weight;
T[i].rchild = T[p2].weight;
T[i].weight = T[p1].weight+T[p2].weight;
}
}
void PrintHFMT(HT T)
{
//输出向量状态表
int i;
printf("\n哈夫曼树的各边显示:\n");
for (i = 0; i < 2 * n - 1; i++)
{
while (T[i].lchild != -1)
{
printf("(%d,%d),(%d,%d)\n", T[i].weight, T[i].lchild, T[i].weight, T[i].rchild);
break;
}
}
}
void hfnode(HT T, int i, int j)
{
//哈夫曼编码函数
j = T[i].parent;
if (T[j].rchild == T[i].weight)
printf("1");
else
printf("0");
if (T[j].parent != -1)
{
i = j, hfnode(T, i, j);
}
}
void huffmannode(HT T)
{
//求哈夫曼编码
int i, j, a;
printf("\n输入的权值的对应哈夫曼编码:");
for (i = 0; i < n; i++)
{
j = 0;
a = i;
printf("\n%i的编码为:", T[i].weight);
hfnode(T, i, j);
i = a;
}
}
void main()
{
HT HT;
CreatHFMT(HT);
PrintHFMT(HT);
huffmannode(HT);
printf("\n ");
}
2)输出结果
4.图
边:无向直接连线(边)
弧:有向直接连线(弧:弧头、弧尾)
4.1 非线性结构
有向图:有n个顶点,有n(n-1)条边
无向图:有n个顶点,有n(n-1)/2条边
顶点的度:一个顶点拥有的边数
有向图:有入度和出度
4.2 存储结构
邻接矩阵
表示顶点之间相邻关系的矩阵。
邻接表
图的一种顺序存储与链式存储结合的存储方法。顶点结点结构:顶点标志域、指针域(指向第一条邻接边)
边结点结构:下标域(邻接顶点的)、指针域(指向第一条邻接边)
优点:通过某顶点查找以该顶点为弧尾的弧结点很方便。
缺点:通过该顶点查找以其为弧头的弧结点需要遍历整张邻接表。
注:逆邻接矩阵的优缺点和邻接矩阵相反。
十字链表优点:方便通过某顶点能够同时查找以该顶点为弧尾和弧头的弧结点。
4.3 遍历方式
图的遍历方式:
深度优先搜索(DFS,类似树的先序遍历):
一条路走到黑,只要有顶点就一直走,没有就向后到,直到所有顶点访问
广度优先搜索(BFS,类似树的层次遍历):
层次,一层一层来,每层从左边开始连通性
最小生成树
构造算法:Prim算法、Kruskal算法
最短路径
算法:迪杰斯特拉Dijkstra算法
就是从起点出发,分别找分支处最小的方向进行,尽头终止,返回,
Floyd算法
有向无环图
有向无环图(不存在环的有向图)
应用:
·拓扑排序·关键路径
1)排序sort.c
/*===============================================
* 文件名称:sort.c
* 创 建 者:箫声.
* 创建日期:2024年03月28日
* 描 述:排序操作(从小到大)
================================================*/
#include <stdio.h>
typedef int data_t;
int bin_search(data_t *data, int size, data_t key)
{
int mid, high, low;
low = 0;
high = size-1;
while (low <= high)
{
mid = (low + high) / 2;
if (key == data[mid])
return mid;
else if (key > data[mid])
low = mid + 1;
else
high = mid - 1;
}
return -1;
}
int main(int argc, char *argv[])
{
data_t data[10] = {3, 5, 9, 20, 56, 78, 89, 100, 123, 150};
printf("pos: %d\n", bin_search(data, 10, 87));
printf("pos: %d\n", bin_search(data, 10, 20));
return 0;
}
2)输出结果
2) 领接表(Vex.c)
/*===============================================
* 文件名称:Vex.c
* 创 建 者:箫声.
* 创建日期:2024年03月28日
* 描 述:创建图进行邻接表拓扑排序
================================================*/
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTEX_NUM 20//最大顶点个数
#define VertexType int//顶点数据的类型
typedef enum{false,true} bool;
typedef struct ArcNode{
int adjvex;//邻接点在数组中的位置下标
struct ArcNode * nextarc;//指向下一个邻接点的指针
}ArcNode;
typedef struct VNode{
VertexType data;//顶点的数据域
ArcNode * firstarc;//指向邻接点的指针
}VNode,AdjList[MAX_VERTEX_NUM];//存储各链表头结点的数组
typedef struct {
AdjList vertices;//图中顶点及各邻接点数组
int vexnum,arcnum;//记录图中顶点数和边或弧数
}ALGraph;
//找到顶点对应在邻接表数组中的位置下标
int LocateVex(ALGraph G,VertexType u){
for (int i=0; i<G.vexnum; i++) {
if (G.vertices[i].data==u) {
return i;
}
}return -1;
}
//创建 AOV 网,构建邻接表
void CreateAOV(ALGraph **G){
*G=(ALGraph*)malloc(sizeof(ALGraph));
scanf("%d,%d",&((*G)->vexnum),&((*G)->arcnum));
for (int i=0; i<(*G)->vexnum; i++) {
scanf("%d",&((*G)->vertices[i].data));
(*G)->vertices[i].firstarc=NULL;
}
VertexType initial,end;
for (int i=0; i<(*G)->arcnum; i++) {
scanf("%d,%d",&initial,&end);
ArcNode *p=(ArcNode*)malloc(sizeof(ArcNode));
p->adjvex=LocateVex(*(*G), end);
p->nextarc=NULL;
int locate=LocateVex(*(*G), initial);
p->nextarc=(*G)->vertices[locate].firstarc;
(*G)->vertices[locate].firstarc=p;
}
}
//结构体定义栈结构
typedef struct stack{
VertexType data;
struct stack * next;
}stack;
//初始化栈结构
void initStack(stack* *S){
(*S)=(stack*)malloc(sizeof(stack));
(*S)->next=NULL;
}
//判断链表是否为空
bool StackEmpty(stack S){
if (S.next==NULL) { return true;
}return false;
}
//进栈,以头插法将新结点插入到链表中
void push(stack *S,VertexType u){
stack *p=(stack*)malloc(sizeof(stack));
p->data=u;
p->next=NULL;
p->next=S->next;
S->next=p;
}
//弹栈函数,删除链表首元结点的同时,释放该空间,并将该结点中的数据域通过地址传值给变量 i;
void pop(stack *S,VertexType *i){
stack *p=S->next;
*i=p->data;
S->next=S->next->next;
free(p);
}
//统计各顶点的入度
void FindInDegree(ALGraph G,int indegree[]){
//初始化数组,默认初始值全部为 0
for (int i=0; i<G.vexnum; i++) {
indegree[i]=0;
}
//遍历邻接表,根据各链表中结点的数据域存储的各顶点位置下标,在 indegree 数组相应位置+1
for (int i=0; i<G.vexnum; i++) {
ArcNode *p=G.vertices[i].firstarc;
while (p) {
indegree[p->adjvex]++;
p=p->nextarc;
}
}
}
void TopologicalSort(ALGraph G){
int indegree[G.vexnum];//创建记录各顶点入度的数组
FindInDegree(G,indegree);//统计各顶点的入度
//建立栈结构,程序中使用的是链表
stack *S;
initStack(&S);
//查找度为 0 的顶点,作为起始点
for (int i=0; i<G.vexnum; i++) {
if (!indegree[i]) {
push(S, i);
}
}
int count=0;
//当栈为空,说明排序完成
while (!StackEmpty(*S)) {
int index;
//弹栈,并记录栈中保存的顶点所在邻接表数组中的位置
pop(S,&index);
printf("%d",G.vertices[index].data);
++count;
//依次查找跟该顶点相链接的顶点,如果初始入度为 1,当删除前一个顶点后,该顶点入度为 0
for (ArcNode *p=G.vertices[index].firstarc; p; p=p->nextarc) {
VertexType k=p->adjvex;
if (!(--indegree[k])) {
//顶点入度为 0,入栈
push(S, k);
}
}
}
//如果 count 值小于顶点数量,表明该有向图有环
if (count<G.vexnum) {
printf("该图有回路"); return;
}
}
int main(){
ALGraph *G;
CreateAOV(&G);//创建 AOV 网
TopologicalSort(*G);//进行拓扑排序
return 0;
}
输出结果
3) 领接矩阵(maraph.c)
/*===============================================
* 文件名称:3tuzheng.c
* 创 建 者: 肖立生
* 创建日期:2022年07月23日
* 描 述:领接矩阵
================================================*/
#include <stdio.h>
#define MAX 100
typedef struct{
int n,e;
char vexs[MAX];
int edges[MAX][MAX];
}mgraph;
void mgraph_create(mgraph *g)
{
int i,j,k;
char ch1,ch2;
printf("please input dingdianshu:");
scanf("%d",&g->n);
printf("please input bianshu:");
scanf("%d",&g->e);
printf("please input vertex information:\n");
for(i = 0;i<g->n;i++)
{
getchar();
printf("input %d number:",i+1);
scanf("%c",&(g->vexs[i]));
}
for(i = 0;i< g->n;i++)
for(j = 0;j <g->n;j++)
g->edges[i][j] = 0;
for(k = 0;k < g->e;k++)
{
getchar();
printf("create %d bian:",k+1);
scanf("%c,%c",&ch1,&ch2);
for(i =0;i<g->n;i++)
for(j = 0; j< g->n;j++)
if(ch1 == g->vexs[i]&&ch2==g->vexs[j])
{
g->edges[i][j] =1;
g->edges[j][i] =1;
}
}
}
void mgraph_disp(mgraph g)
{
int i,j;
printf("\n图的邻接矩阵:\n");
for(i = 0;i <g.n;i++)
{
for(j = 0; j < g.n;j++)
printf("%5d",g.edges[i][j]);
printf("\n");
}
}
void main(int argc, char *argv[])
{
mgraph g;
mgraph_create(&g);
mgraph_disp(g);
}
输出结果
8.查找
1. 静态查找法
1.1 顺序查找
基本思想:
从表的一端开始扫描线性表,依次按给定值与关键字进行比较,若相等,则查找成功,并给出数据元素在表中的位置;若整个表查找完毕,仍未找到与给定值相等的关键字,则查找失败,给出失败信息。
时间复杂度:查找长度的量级(O(n))
优点:对表中数据元素的存储没有要求。
缺点:当n很大时,平均查找长度较大,效率低。只能进行顺序查找(线性链表)
1.2 二分查找
基本思想:
在有序表中,取中间元素作为比较对象,若给定值与中间元素的关键字相等,则查找成功;若给定值小于中间元素的关键字,则在中间元素的左半区继续查找;若给定值大于中间元素的关键字,则在中间元素的右半区继续查找。不断重复上述查找过程,直到查找成功,或所查找的区域无数据元素,查找失败。
时间复杂度:O(log2^n)
优点:效率高
缺点:
·必须按关键字排序,有时排序也很费时。
·只适用顺序存储结构,进行插入、删除操作必须移动大量的结点。
1.3 分块查找
基本思想:
将具有n个元素的主表分成m个块(又称子表),每块内的元素可以无序,但要求块与块之间必须有序,并建立索引表。索引表包括两个字段:关键字字段(存放对应块中的最大关键字值)和指针字段(存放指向对应块的首地址)。查找方法如下:
(1)在索引表中检测关键字字段,以确定待找值kx所处的分块(可用二分查找)位置。
(2)根据索引表指示的首地址,在该块内进行顺序查找。
1)示例代码
/*===============================================
* 文件名称:bin_search.c
* 创 建 者:箫声.
* 创建日期:2023年03月23日
* 描 述:折半查找
================================================*/
#include <stdio.h>
typedef int data_t;
int bin_search(data_t *data, int size, data_t key)
{
int mid, high, low;
low = 0;
high = size-1;
while (low <= high)
{
mid = (low + high) / 2;
if (key == data[mid])
return mid;
else if (key > data[mid])
low = mid + 1;
else
high = mid - 1;
}
return -1;
}
int main(int argc, char *argv[])
{
data_t data[10] = {3, 5, 9, 20, 56, 78, 89, 100, 123, 150};
printf("pos: %d\n", bin_search(data, 10, 87));
printf("pos: %d\n", bin_search(data, 10, 20));
return 0;
}
2)输出结果
2.动态查找表
2.1 二叉排序树
二叉排序树是空树或者具有以下性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于根结点的值。
(2)若右子树不空,则右子树上所有结点的值均大于根结点的值。
(3) 左、右子树也都是二叉排序树。
基本思想:若查找树为空,查找失败。
查找树非空,将给定值与杳找树的根结点关键字比较。
若相等,查找成功,结束查找过程,否则:
·当给定值小于根结点关键字时,查找将在左子树上继续进行,转到①。
·当给定值大于根结点关键字时,查找将在右子树上继续进行,转到①。
时间复杂度:(n+1)/2(最差)
2.2 平衡二叉树
平衡二叉树是指树中任一结点的左、右子树高度大致相等的二叉树。平衡二叉树有很多种,最著名的是AVL树。平衡二叉树是一棵空树或者是具有以下性质的二叉排序树:
(1)它的左子树和右子树的高度之差(称为平衡因子)的绝对值不超过1。
(2)它的左子树和右子树又都是平衡二叉树。解决插入结点后失去平衡:
不平衡子树形态为LL型。
不平衡子树形态为LR型。
不平衡子树形态为RR型。
不平衡子树形态为RL型。
3.哈希表
哈希查找也称为散列查找,它既是一种查找方法,又是一种存储方法(散列存储)。散列存储的内存存放形式称为散列表(哈希表)。
散列查找与前述的方法不同,数据元素的存储位置与关键字之间不存在确定的关系,也不需要进行一系列的关键字查找比较。它是依据关键字直接得到其对应的数据元素位置,即要求关键字与数据元素间存在一一对应的关系。通过这个关系,很快地由关键字得到对应的数据元素位置。
哈希函数的构造方法1)直接定址法
2)除留余数法
处理冲突的方法
1)开放定址法线性探测法
二次探测法(平方探测法)
2)拉链法(链地址法)
1)哈希示例(hash_search.c)
/*===============================================
* 文件名称:hash_search.c
* 创 建 者:箫声.
* 创建日期:2024年03月23日
* 描 述:哈希查找
================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int data_t;
typedef struct node{
data_t data;
struct node *next;
}node_t;
void hash_create(data_t *data, int size, node_t *hp[], int n)
{
int i, pos;
for (i = 0; i < size; i++)
{
node_t *p = (node_t *)malloc(sizeof(node_t));
memset(p, 0, sizeof(node_t));
p->data = data[i];
p->next = NULL;
pos = data[i] % n;
if (NULL == hp[pos])
hp[pos] = p;
else
{
p->next = hp[pos];
hp[pos] = p;
}
}
}
void hash_display(node_t *hp[], int n)
{
int i;
for (i = 0; i < n; i++)
{
printf("%d: ", i);
node_t *p = hp[i];
while (p != NULL)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
}
int main(int argc, char *argv[])
{
data_t data[11] = {23, 34, 14, 38, 46, 16, 68, 15, 7, 31, 26};
node_t *hp[13] = {NULL};
hash_create(data, 11, hp, 13);
hash_display(hp, 13);
return 0;
}
2)输出结果
9.排序
1)冒泡排序
2)选择排序
3)直接插入排序
4)快速排序
三、往期回顾
2. C语言基础练习题总结(嵌入式系列学习2)-CSDN博客
3. Linux基础、C语言高级基础知识总结(嵌入式系列学习3)-CSDN博客
持续更新中.......
更多文章,点击左上角头像,查看个人简介和更多相关项目的分享,也可通过关注我的其他平台,一起学习,一起进步!Come on!!!动起手来,开始探索~~~