数据结构
一、什么是数据结构
1.数据结构的起源
1968年 , 美国高德纳教授,《计算机程序设计艺术》第一卷《基本算法》,开创了数据结构和算法的先河
数据结构是研究数据之间和操作的学科,而非计算方法
数据结构+算法=程序 美国沃斯提出 这句话揭示了程序的本质
2.数据结构相关基础概念
数据:所有能够输入到计算机中,能够被程序处理的描述客观事物的符号
数据项:有独立含义的数据的最小单位,也称为域
数据元素:组成数据的有一定含义的基本单位,也成为结点、节点、记录
数据结构:相互之间存在一种或多种特定关系的数据元素的集合
算法:数据结构中所具备的功能、解决某种特定问题的方法
3.数据结构的三个方面
数据的逻辑关系
数据的存储关系
数据结构的运算
二、数据结构的逻辑和存储关系
1.逻辑关系:
集合:数据元素同属于同一个集合,但元素之间没有任何关系
线性结构:数据元素之间存在一对一关系
树形结构:数据元素之间存在一对多关系
图型结构:数据元素之间存在多对多关系
2.存储(物理)关系:
顺序结构:数据存储在连续的内存中,用数据元素的相对位置来表示关系
优点:支持随机访问、访问查找效率极高、适合查找数据频繁的结构
缺点:插入、删除时效率低、不方便,内存空间利用率低、要求高
链式结构:数据元素存储在彼此相互独立的内存中,每个独立的元素也叫做节点,每个结点中增加一项数据
项用于存储其他相关节点的地址,以此表示结点之间的关系
优点:插入、删除更方便,空间利用率极高、对内存要求不高
缺点:不支持随机访问、只能从头到尾逐个访问
注意:逻辑结构和存储结构采用哪种根据实现难度、空间时间要求、操作习惯等方面综合考虑选择合适的结构
逻辑结构与存储结构的关系:
线性表 顺序 链式
树 链式 顺序
图 顺序+链式
三、数据结构的运算
1.创建数据结构 create creat
2.销毁数据结构 destroy
3.清空数据结构 clean
4.数据结构排序 sort
5.插入元素 insert
6.删除元素 delete
7.访问元素 access
8.查询元素 query
9.修改元素 modify
10.遍历数据结构 ergodic show print
四、顺序表和链式表实现
顺序表
数据项:
存储元素的连续内存首地址
表的容量
当前元素的数量
运算:
创建、销毁、清空、插入、删除、访问、查询、修改、排序、遍历
注意:
1.要保持数据元素的连续性
2.不要越界
//设计顺序表结构 #include#define TYPE int #include #include #include typedef struct Array { TYPE* prt; //存储元素的内存首地址 size_t cal; //表的容量 size_t cnt; //已存储元素的数量 }Array; //创建 Array* create_array(size_t size); { // 分配顺序表结构的内存 Array* arr = malloc(sizeof(Array)); // 分配存储元素的内存 arr->ptr = malloc(sizeof(TYPE)*size); // 记录容量 arr->cal = size; // 初始化元素的数量 arr->cnt = 0; return arr; } //销毁 void destroy_array(Array* arr); { free(arr->ptr); free(arr); } //清空 void clean(Array* arr); { arr->cnt=0; } //插入 bool insert_array(Array* arr,int index,TYPE val); { // 判断表是否已满 if(arr->cnt >= arr->cal) return false; // 判断下标是否能让表保持连续性 if(index > arr->cnt) return false; memmove(arr->ptr+index+1,arr->ptr+index,(arr->cnt-index)*sizeof(TYPE)); arr->ptr[index] = val; arr->cnt++; return true; } //删除 bool delete_array(Array* arr,size_t index); { if(index >= arr->cnt) return false; memmove(arr->ptr+index,arr->ptr+index+1,(arr->cnt-index-1)*sizeof(TYPE)); arr->cnt--; return true; } //访问 bool access_array(Array* arr,size_t index,TYPE* val); { if(index >= arr->cnt) return false; *val = arr->ptr[index]; return true; } //查询 int query_array(Array* arr,TYPE val); { for(int i=0;icnt;i++) { if(val == arr->ptr[i]) return i; } return -1; } //修改 bool modify_array(Array* arr,int index,TYPE val); { if(index >= arr->cnt) return false; arr->ptr[index] = val; return true; } //排序 void sort_array(Array* arr); { for(int i=0;icnt-1;i++) { for(int j=i+1;jcnt;j++) { if(arr->ptr[i]>arr->ptr[j]) TYPE temp = arr->ptr[i]; arr->ptr[i] =arr->ptr[j]; arr->ptr[j] = temp; } } } //遍历 void show_array(Array* arr); { for(int i=0;icnt;i++) { printf("&d ",arr->ptr[i]); } printf("n"); } int main() { Array* arr = create_array(10); for(int i=0;i<5;i+=) { insert_array(arr,0,i+1); } show_array(arr); insert_array(arr,5,8); show_array(arr); delete_array(arr,3) TYPE num = 0; access_array(arr,0,&num); printf("num=%dn",num); printf(""); sort_array(arr); show_array(arr); destroy_array(arr); arr NULL; }
链式表:list
元素(结点)的数据项:
数据项:可以是任何类型的若干个数据项
指针域:指向下一个结点
由若干个结点通过指针域连接到一起就形成了链表
不带头结点的单链表:
第一个结点的数据域存储有效数据
缺点:添加、删除结点时,可能会修改指向第一个结点的指针,参数需要使用二级指针,才能更改指针的指向,比较麻烦
#include#include #include #define TYPE int // 设计链表的结点 typedef struct Node { TYPE data; //结点数据域 struct Node* next; //结点的指针域 }Node; // 创建结点 Node* create_node(TYPE data) { // 分配结点所需内存 Node* node = malloc(sizeof(Node)); nade->data = data; node->next = NULL; return node; } // 头添加 void add_head_list(Node** head, TYPE data) { Node* node = create_node(data); node->next = *head; *head = node; } // 按值删除 bool del_value_list(Node** head,TYPE data) { if(data == (*head)->data) { Node* temp = *head; *head = temp->next; free(teamp); return true; } for(Node* n=*head;NULL != n->next;n=n->next) { // 要找的是待删除结点的上一个结点n if(data == n->next->data) { //备份待删除结点 Node* teamp = n->next; //连接待删除结点上一个和下一个 n->next = temp->next; //释放待删除结点内存 free(teamp); return true; } } return false; } //按位置删除 bool del_index_list(Node** head,size_t index) { if(0 == index) { Node* teamp = *head; *head = teamp->next; free(teamp); return true; } // n是待删除结点上一个 Node* n = *head; while(0 <--index) { n = n->next; if(NULL == n->next) return false; } // 找到了待删除结点 n->next Node* temp = n->next; n->next = temp->next; free(temp); return true; } //排序 void sort_list(*Node head) { for(Node* n=head;NULL!=n->next;n=n->next) { for(Node* m=n->next;NULL!=m;m=m->next) { if(n->data>m->data) { TYPE teamp = n->data; n->data = m->data; m->data = teamp; } } } } //访问 bool access_list(Node* head,int index,TYPE* val) { Node* node = head; for(int i=0;i next; if(NULL == node) return false; } *val = node->data; return true; } // 遍历 void show_list(Node* head) { for(Node* n=head; NULL!=n;n=n->next) { printf("%d",n->data); } printf("n"); } int main() { Node* head = NULL; for(int i=0;i<5;i++) { add_head_list(&head,i+1); } show_list(head); del_value_list(&head,5); show_list(head); del_index_list(&head,0); show_list(head); } 带头结点的单链表: 第一个结点的数据域不存储有效元素,仅仅只是使用它的指针域永远指向链表的第一个数据有效的结点 优点:添加、删除时会比不带头结点的链表更方便 注意:其他的操作要从头结点的下一个结点开始 #include #include #include #define TYPE int // 设计链表的结点 typedef struct Node { TYPE data; //结点数据域 struct Node* next; //结点的指针域 }Node; // 创建结点 Node* create_node(TYPE data) { // 分配结点所需内存 Node* node = malloc(sizeof(Node)); nade->data = data; node->next = NULL; return node; } //头添加 void add_head_list(Node* head,TYPE val); { Node* node = create_node(val); node->next = head->next; head->next = node; } //尾添加 void add_tail_list(Node* head,TYPE val); { Node* node = create_node(val); // n找到最后一个结点 Node* n = head; while(NULL != n->next) n = n->next; n->next = node; } //按值删除 bool del_value_list(Node* head,TYPE val); { for(Node* n=head;NULL != n->next;n=n->next) { // 要找的是待删除结点的上一个结点n if(val == n->next->data) { //备份待删除结点 Node* teamp = n->next; //连接待删除结点上一个和下一个 n->next = temp->next; //释放待删除结点内存 free(teamp); return true; } } return false; } //按位置删除 bool del_index_list(Node* head,size_t index); { // n是待删除结点上一个 Node n = head; if(NULL == n->next ) return false; while(i=0;i next; if(NULL == n->next) return false; } // 找到了待删除结点 n->next Node* temp = n->next; n->next = temp->next; free(temp); return true; } //插入 bool insert_list(Node* head,size_t index,TYPE val); { Node* n = head; // 不进行头添加 if(NULL == n->next || inde == 0) return false; for(int i = 0;i next; // 不进行尾添加 if(NULL == n->next) return false; } Node* node = create_node(val); node->next = n->next; n->next = node; return true; } //修改 bool modify_list(Node* head,TYPE old,TYPE val); { for(Node* n=head->next;NULL != n;n=n->next) { if(old == n->data) { n->data = val; return true; } } return false; } //访问 bool access_list(Node* head,size_t index,TYPE* val); { Node* n = head->next; if(NULL == n) return false; for(int i=0;i next; if(NULL == n) return false; } *val = n->data; return true; } //查找 int query_list(Node* head,TYPE val); { Node* n = head->next; for(int i=0;NULL != n;i++ ) { if(n->data == val) return i; n = n->next } return -1; } //排序 void sort_list(Node* head); { for(Node* n=head->next;NULL!=n->next;n=n->next) { for(Node* m=n->next;NULL!=m;m=m->next) { if(n->data>m->data) { TYPE teamp = n->data; n->data = m->data; m->data = teamp; } } } } void show_list(Node* head) { for(Node* n = head->next;NULL != n;n = n->next) { printf("%d ",n->data); } printf("n"); } int main() { // head就是带头结点链表的头结点,head->next永远指向第一个有效数据的结点 Node* head = create_node(0); for(int i=0;i<10;i++) { add_head_list(head,rand()%100); } }



