数据结构
一、什么是数据结构
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 <stdio.h>
#define TYPE int
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
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;
/*
for(int i=arr->cnt;i>index;i--)
{
arr->ptr[i] = arr->ptr[i-1];
}
*/
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;i<arr->cnt;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;i<arr->cnt-1;i++)
{
for(int j=i+1;j<arr->cnt;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;i<arr->cnt;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=%d\n",num);
printf("");
sort_array(arr);
show_array(arr);
destroy_array(arr);
arr NULL;
}
链式表:list
元素(结点)的数据项:
数据项:可以是任何类型的若干个数据项
指针域:指向下一个结点
由若干个结点通过指针域连接到一起就形成了链表
不带头结点的单链表:
第一个结点的数据域存储有效数据
缺点:添加、删除结点时,可能会修改指向第一个结点的指针,参数需要使用二级指针,才能更改指针的指向,比较麻烦
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#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<index;i++)
{
node = node->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);
/*
Node* n1 = create_node(1);
Node* n2 = create_node(2);
Node* n3 = create_node(3);
Node* n4 = create_node(4);
Node* n5 = create_node(5);
n1->next = n2;
n2->next = n3;
n3->next = n4;
n4->next = n5
for(Node* n=n1;n!=NULL;n=n->next)
{
printf("%d ",n->data);
}
*/
}
带头结点的单链表:
第一个结点的数据域不存储有效元素,仅仅只是使用它的指针域永远指向链表的第一个数据有效的结点
优点:添加、删除时会比不带头结点的链表更方便
注意:其他的操作要从头结点的下一个结点开始
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#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<index;i++)
{
n = n->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<index;i++)
{
n = n->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<index;i++)
{
n=n->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);
}
}