基础知识
数据结构的三要素: 逻辑结构、物理结构、数据的运算
逻辑结构:集合、线性结构、树形结构、图形或网状型结构
物理结构(存储结构):顺序存储、链式存储、索引存储、散列存储
数据的运算:施加在数据上的运算包括运算的定义和实现。运算的定义是针对逻辑结构的,指出运算的功能;运算的实现是针对存储结构的,指出运算的具体操作步骤。
集合:结构中的数据元素之间除“同属一个集合”外,别无关系。
线性关系:结构中的数据元素之间只存在一对一的关系。
树形结构:结构中的数据元素存在一对多的关系。
图状或网状结构:结构中的数据元素之间存在多对多的关系。
顺序存储:逻辑上相邻的元素在物理上也相邻。
优点:随机存取、每个元素占用最少的存储空间
缺点:只能使用相邻的存储单元,因此可能产生较多的外部碎片
链式存储:不要求逻辑上相邻的元素在屋里位置上也相邻。
优点:不会出现碎片现象,能充分利用所有存储单元
缺点:只能顺序存储,指针占用空间
索引存储:存储信息的同时,也建立索引表。
优点:可以实现随机存取
缺点:索引表会占用空间,同时修改索引表会花费较多的时间
散列存储:根据元素的关键字直接计算出该元素的存储地址。
优点:检索、增加和删除结点的时间复杂度较低
缺点:散列函数不好,则会出现元素存储单元的冲突,而解决冲突会增加时间和空间的开销
算法的5个重要特性
有穷性:一个算法必须总在执行有穷步之后结束,且每一步都可在有穷时间内完成。
确定性:算法中每条指令必须有确切的含义,对于相同的输入只能得出相同的输出。
可行性:算法中描述的操作都可以通过已经实现的基本运算执行有限次来实现。
输入:一个算法有零个或多个输入
输出:一个算法有一个或多个输出
一个“好"的算法要达到的目标
正确性:算法应能够正确地解决求解问题。
可读性:算法应具有良好的可读性,以帮助人们理解。
健壮性:输入非法数据时,算法能适当地做出反应或进行处理,而不会产生莫名其妙的
输出结果。
效率与低存储量需求:效率是指算法执行的时间,存储量需求是指算法执行过程中所需
要的最大存储空间,这两者都与问题的规模有关。
顺序表
#include<iostream>
#define max 100
//顺序表
typedef struct {
int data[100];
int length = 0;
}List;
//方法
// 判空
bool Empty(List L) {
if (L.length == 0) return true;
return false;
}
//判满
bool Full(List L) {
if (L.length == max) return true;
return false;
}
//插入操作:将e插入到顺序表的第i个位置上
void Insert(List& L, int i, int e) {
//i越界,则返回
if (i < 1 || i > max) {
printf("i越界");
return;
}
//如果表满,则返回
if (Full(L) == true) {
printf("表满");
return;
}
//否则插入
for (int k = L.length; k >= i; k--) {
L.data[k] == L.data[k - 1];
}
L.data[i - 1] = e;
L.length++;
}
//删除操作:将顺序表中第i个位置的元素删除,并通过实参e返回
void Delete(List& L, int i, int& e) {
//表空
if (Empty(L) == true) {
printf("表空");
return;
}
//i越界
if (i > L.length) {
printf("i越界");
return;
}
//否则删除
e = L.data[i - 1];
for (int k = i; k < L.length; k++) {
L.data[k - 1] = L.data[k];
}
L.length--;
}
//遍历
void Trace(List L) {
if (Empty(L)) printf("空的");
for (int i = 0; i < L.length; i++) {
printf("%d ", L.data[i]);
}
}
int main() {
List L;
for (int i = 1; i < 10; i++) {
Insert(L, i, i);
}
Trace(L);
printf("\n");
int e = 0;
for (int i = 1; i < 10; i++) {
Delete(L, 1, e);
}
Trace(L);
}
单链表
#include<iostream>
//单链表
typedef struct Node {
int data;
struct Node* next; //此处与上方结构体名相同
}Node,*List;
//初始化
void Init(List& L) {
L = (List)malloc(sizeof(Node));
L->next = NULL;
}
//判空
bool Empty(List L) {
if (L->next == NULL) return true;
return false;
}
//头插法
void Insert1(List& L, int e) {
Node* p = (Node*)malloc(sizeof(Node));
p->data = e;
p->next = NULL;
if (Empty(L)) {
L->next = p;
return;
}
p->next = L->next;
L->next = p;
}
//尾插法
void Insert2(List& L, int e) {
Node* p = L;
//寻找最后一个结点
while(p->next != NULL) {
p = p->next;
}
p->next = (Node*)malloc(sizeof(Node));
p = p->next;
p->next = NULL;
p->data = e;
}
//按位插入
void Insert3(List& L, int k, int e) {
Node* p = L;
k--;
//找到插入位置前一个结点
while (k--) {
if (p != NULL) p = p->next;
else {
printf("i越界");
return;
}
}
//创造新结点
Node* q = (Node*)malloc(sizeof(Node));
q->data = e;
q->next = p->next;
p->next = q;
}
//删除操作
void Delete(List& L, int k,int &e) {
if (Empty(L)) return;
Node* p = L;
k--;
//找到插入位置前一个结点
while (k--) {
if (p != NULL) p = p->next;
else {
printf("i越界");
return;
}
}
//越界边界条件
if (p->next == NULL) return;
//删除结点
Node* q = p->next;
p->next = q->next;
e = q->data;
free(q);
}
//遍历操作
void Trace(List L) {
Node* p = L->next;
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
}
int main() {
List L;
Init(L);
//头插法测试:插入12345 --- 54321
for (int i = 1; i <= 5; i++) {
Insert1(L, i);
}
Trace(L);
//尾插法测试
for (int i = 1; i <= 5; i++) {
Insert2(L, i);
}
printf("\n");
Trace(L);
//删除测试
int e;
for (int i = 1; i <= 5; i++) {
Delete(L, 1, e);
}
printf("\n");
Trace(L);
}
循环单链表
#include<iostream>
//循环单链表
typedef struct Node {
int data;
struct Node* next;
}Node,*List;
//初始化
void init(List& L) {
L = (List)malloc(sizeof(Node));
L->next = L;
}
//判空
bool Empty(List& L) {
if (L->next == L) return true;
return false;
}
//长度
int Length(List& L) {
int res = 0;
Node* p = L->next;
if (p != L) {
p = p->next;
res++;
}
return res;
}
//头插法
void Insert1(List& L, int e) {
Node* p = L;
Node* q = (Node*)malloc(sizeof(Node));
q->data = e;
q->next = p->next;
p->next = q;
}
//按位插入
void Insert2(List& L, int k, int e) {
Node* p = L;
Node* q = (Node*)malloc(sizeof(Node));
q->data = e;
k--;
while (k--) {
p = p->next;
}
q->next = p->next;
p->next = q;
}
//删除操作
void Delete(List& L, int k) {
if (Empty(L)) return;
int len = Length(L);
if (k > len) return;
Node* p = L;
k--;
while (k--) {
p = p->next;
}
Node* q = p->next;
p->next = q->next;
free(q);
}
void Trace(List L) {
Node* p = L->next;
while (p != L) {
printf("%d ", p->data);
p = p->next;
}
}
int main() {
List L;
init(L);
//头插法测试
for (int i = 0; i < 5; i++) {
Insert1(L, i);
}
Trace(L);
printf("\n");
//插入操作测试
for (int i = 6; i < 9; i++) {
Insert2(L, 1, i);
}
Trace(L);
printf("\n");
//删除操作
for (int i = 0; i < 3; i++) {
Delete(L, 1);
}
Trace(L);
printf("\n");
}
双链表
#include<iostream>
//双链表
typedef struct Dnode {
int data;
struct Dnode* prior, * next;
}Dnode,*DLink;
int length(DLink L) {
Dnode* p = L;
int res = 0;
while (p->next != NULL) {
p = p->next;
res++;
}
return res;
}
bool Empty(DLink L) {
if (L->next == NULL) return true;
return false;
}
//插入操作
void Insert(DLink& L, int i, int e) {
int len = length(L);
if (len + 1< i || i < 1) return;
Dnode* p;
p = L;
i--;
while (i--) {
p = p->next;
}
Dnode* q = (Dnode*)malloc(sizeof(Dnode));
q->data = e;
q->next = q->prior = NULL;
if (p->next == NULL) {
p->next = q;
q->prior = p;
}
else {
p->next->prior = q;
q->next = p->next;
p->next = q;
q->prior = p;
}
}
//删除操作
void Delete(DLink& L, int i) {
if (Empty(L)) return;
if (i > length(L) || i < 1) return;
Dnode* p;
p = L;
i--;
while (i--) {
p = p->next;
}
Dnode* q = p->next;
p->next = q->next;
q->next->prior = p;
free(q);
}
//遍历
void Trace(DLink L) {
Dnode* p = L->next;
while (p) {
printf("%d ", p->data);
p = p->next;
}
}
int main() {
DLink L = (Dnode*)malloc(sizeof(Dnode));
L->next = NULL;
L->prior = NULL;
for (int i = 0; i < 5; i++) {
Insert(L, i + 1, i);
}
Trace(L);
for (int i = 0; i < 3; i++) {
Delete(L, 1);
}
Trace(L);
}