数据的存储结构
-
线性结构
连续存储[数组]
离散存储[链表]
线性结构的应用1--栈
线性结构的应用2--队列
-
非线性结构
树
图
线性结构
把所有的节点用一根直线穿起来
-
连续存储[数组]
-
定义:元素类型相同,所占内存大小相等。
-
优缺点:存取的速度很快,事先必须知道数组的长度,插入删除元素很慢,空间通常有限制,需要大块连续的内存块。
示例:
#include <stdio.h>
#include <malloc.h> //包含了malloc函数
#include <stdlib.h> //包含了exit函数
//定义了一个数据类型, 该数据类型的名字叫做struct Arr, 该数据类型含有三个成员, 分别是pBase, len, cnt
struct Arr
{
int* pBase; //存储的是数组第一个元素的地址
int len; //数组所能容纳的最大元素的个数
int cnt; //当前数组有效元素的个数
};
void init_arr(struct Arr* pArr, int length); //分号不能省
bool append_arr(struct Arr* pArr, int val); //追加
bool insert_arr(struct Arr* pArr, int pos, int val); //pos的值从1开始
bool delete_arr(struct Arr* pArr, int pos, int* pVal);
bool is_empty(struct Arr* pArr);
bool is_full(struct Arr* pArr);
void sort_arr(struct Arr* pArr);
void show_arr(struct Arr* pArr);
void inverse_arr(struct Arr* pArr);
int main(void)
{
struct Arr arr;
int val;
init_arr(&arr, 6);
show_arr(&arr);
append_arr(&arr, 1);
append_arr(&arr, 7);
append_arr(&arr, 3);
insert_arr(&arr, 1, 2);
if (delete_arr(&arr, 2, &val))
{
printf("删除成功1\n");
printf("您删除的元素是: %d\n", val);
}
else
{
printf("删除失败!\n");
}
show_arr(&arr); //显示追加和删除之后的内容
inverse_arr(&arr);
show_arr(&arr); //显示倒置后的内容
sort_arr(&arr);
show_arr(&arr); //显示排序后的内容
return 0;
}
void init_arr(struct Arr* pArr, int length)
{
pArr->pBase = (int*)malloc(sizeof(int) * length);
if (NULL == pArr->pBase)
{
printf("动态内存分配失败!\n");
exit(-1); //终止整个程序
}
else
{
pArr->len = length;
pArr->cnt = 0;
}
return;
}
bool is_empty(struct Arr* pArr)
{
if (0 == pArr->cnt)
return true;
else
return false;
}
bool is_full(struct Arr* pArr)
{
if (pArr->cnt == pArr->len)
return true;
else
return false;
}
void show_arr(struct Arr* pArr)
{
if (is_empty(pArr))
{
printf("数组为空!\n");
}
else
{
for (int i = 0; i < pArr->cnt; ++i)
printf("%d ", pArr->pBase[i]); //int *
printf("\n");
}
}
bool append_arr(struct Arr* pArr, int val)
{
//满是返回false
if (is_full(pArr))
return false;
//不满时追加
else
{
pArr->pBase[pArr->cnt] = val;
pArr->cnt++;
return true;
}
}
bool insert_arr(struct Arr* pArr, int pos, int val)
{
if (is_full(pArr))
return false;
if (pos<1 || pos>pArr->cnt + 1)
return false;
for (int i = pArr->cnt - 1; i >= pos - 1; --i)
{
pArr->pBase[i + 1] = pArr->pBase[i];
}
pArr->pBase[pos - 1] = val;
pArr->cnt++;
return true;
}
bool delete_arr(struct Arr* pArr, int pos, int* pVal)
{
if (is_empty(pArr))
return false;
if (pos<1 || pos>pArr->cnt)
return false;
*pVal = pArr->pBase[pos - 1];
for (int i = pos; i < pArr->cnt; ++i)
{
pArr->pBase[i - 1] = pArr->pBase[i];
}
pArr->cnt--;
return true;
}
void inverse_arr(struct Arr* pArr)
{
int i = 0;
int j = pArr->cnt - 1;
int t;
while (i < j)
{
t = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = t;
++i;
--j;
}
return;
}
void sort_arr(struct Arr* pArr)
{
int i, j, t;
for (i = 0; i < pArr->cnt; ++i)
{
for (j = i + 1; j < pArr->cnt; ++j)
{
if (pArr->pBase[i] > pArr->pBase[j])
{
t = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = t;
}
}
}
}
-
离散存储[链表]
-
优缺点:空间没有限制,插入删除元素很快,存取的速度很慢。
-
typedef
示例1:
#include <stdio.h>
typedef int ZHANGSAN; //为int再重新多取一个名字,ZHANGSAN等价于int
typedef struct Student
{
int sid;
char name[100];
char sex;
}ST;
int main(void)
{
//struct Student st;
//struct Student *ps = &st;
int i = 10; //等价于ZHANGSAN i = 10;
ZHANGSAN j = 20;
printf("%d\n", j);
ST st2;
st2.sid = 200;
printf("%d\n", st2.sid);
return 0;
}
示例2:
#include <stdio.h>
typedef struct Student
{
int sid;
char name[100];
char sex;
}*PST; //PST等价于struct Student *
int main(void)
{
struct Student st;
PST ps = &st;
ps->sid = 99;
printf("%d\n", ps->sid);
return 0;
}
示例3:
#include <stdio.h>
typedef struct Student
{
int sid;
char name[100];
char sex;
}*PSTU, STU; //PST等价于struct Student *, STU等价于struct Student
int main(void)
{
STU st; //struct Student st;
PSTU ps = &st; //struct Student *ps = &st;
ps->sid = 99;
printf("%d\n", ps->sid);
return 0;
}
-
链表的定义:n个节点离散分配,彼此通过指针相连,每个节点只有一个前驱节点,每个节点只有一个后驱节点,首节点没有前驱节点,尾节点没有后驱节点。
-
专业术语:
-
首节点:第一个有效节点
-
尾节点:最后一个有效节点
-
头节点:第一个有效节点之前的那个节点,头节点并不存放有效数据,加头节点的目的主要是为了方便对链表的操作。
-
头指针:指向头节点的指针变量
-
尾指针:指向尾节点的指针变量
-
如果希望通过一个函数来对链表进行处理,至少需要接受链表的哪些参数:只需要一个参数即头指针,通过头指针可以推算出链表的其他所有信息。
-
每一个链表节点的数据类型该如何表示:数据域和指针域
typedef struct Node
{
int data; //数据域
struct Node* pNext; //指针域
}NODE, * PNODE; //NODE等价于struct Node, PNODE等价于struct Node *
-
分类:
-
单链表:每一个节点只有一个指针域且只能指向后面的节点
-
双链表:每一个节点有两个指针域
-
循环链表:能通过任何一个节点找到其他所有的节点
-
非循环链表
-
相关算法:
-
遍历
-
查找
-
清空
-
销毁
-
求长度
-
排序
-
删除节点
//删除p所指节点的后面节点
//错误示范,导致内存泄漏
p->pNext = p->pNext->pNext;
//错误示范
free(p->pNext);
//正确方法:先临时定义一个指向p后面节点的指针r
r = p->pNext;
p->pNext = r->pNext;
free(r);
-
插入节点
//把q所指向的节点插到p所指向的节点后面
//错误示范
p->pNext = q;
q->pNext = p->pNex;
//正确方法1:先临时定义一个指向p后面节点的指针r
r = p->pNext;
p->pNext = q;
q->pNext = r;
//正确方法2
q->pNext = p->pNext;
p->pNext = q;
示例:
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define _CRT_SECURE_NO_WARNINGS
typedef struct Node
{
int data; //数据域
struct Node* pNext; //指针域
}NODE, * PNODE; //NODE等价于struct Node, PNODE等价于struct Node *
//函数声明
PNODE create_list(void);
void traverse_list(PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE pHead);
void sort_list(PNODE pHead);
bool insert_list(PNODE pHead, int pos, int val);
bool delete_list(PNODE pHead, int pos, int* pVal);
int main(void)
{
PNODE pHead = NULL; //等价于struct Node *pHead = NULL;
int val;
pHead = create_list(); //create_list()功能: 创建一个非循环单链表, 并将该链表的头节点的地址赋给pHead
traverse_list(pHead);
int len = length_list(pHead);
printf("链表长度是%d\n", len);
if (is_empty(pHead))
printf("链表为空!\n");
else
printf("链表不空!\n");
sort_list(pHead);
traverse_list(pHead);
insert_list(pHead, 2, 3);
traverse_list(pHead);
if (delete_list(pHead, 1, &val))
{
printf("删除成功删除的元素是%d\n", val);
}
else
{
printf("删除失败!您删除的元素不存在!\n");
}
traverse_list(pHead);
return 0;
}
PNODE create_list(void)
{
int len; //用来存放有效节点的个数
int i;
int val; //用来临时存放用户输入的节点的值
//分配了一个不存放有效数据的头节点
PNODE pHead = (PNODE)malloc(sizeof(NODE));
if (NULL == pHead)
{
printf("分配失败,程序终止!\n");
exit(-1);
}
PNODE pTail = pHead;
pTail->pNext = NULL;
printf("请输入您需要生成的链表节点的个数:len = ");
scanf("%d", &len);
for (i = 0; i < len; ++i)
{
printf("请输入第%d个节点的值: ", i + 1);
scanf("%d", &val);
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if (NULL == pHead)
{
printf("分配失败,程序终止!\n");
exit(-1);
}
pNew->data = val;
pTail->pNext = pNew;
pNew->pNext = NULL;
pTail = pNew;
}
return pHead;
}
void traverse_list(PNODE pHead)
{
PNODE p = pHead->pNext;
while (NULL != p)
{
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
return;
}
bool is_empty(PNODE pHead)
{
if (NULL == pHead->pNext)
return true;
else
return false;
}
int length_list(PNODE pHead)
{
PNODE p = pHead->pNext;
int len = 0;
while (NULL != p)
{
++len;
p = p->pNext;
}
return len;
}
void sort_list(PNODE pHead)
{
int i, j, t;
int len = length_list(pHead);
PNODE p, q;
for (i = 0, p = pHead->pNext; i < len - 1; ++i, p = p->pNext)
{
for (j = i + 1, q = p->pNext; j < len; ++j, q = q->pNext)
{
if (p->data > q->data)
{
t = p->data;
p->data = q->data;
q->data = t;
}
}
}
return;
}
//在pHead所指向链表的第pos个节点的前面插入一个新的节点, 该节点的值是val, 并且pos的值是从1开始的
bool insert_list(PNODE pHead, int pos, int val)
{
int i = 0;
PNODE p = pHead;
while (NULL != p && i < pos - 1)
{
p = p->pNext;
++i;
}
if (i > pos - 1 || NULL == p)
return false;
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if (NULL == pNew)
{
printf("动态分配内存失败!\n");
exit(-1);
}
pNew->data = val;
PNODE q = p->pNext;
p->pNext = pNew;
pNew->pNext = q;
return true;
}
bool delete_list(PNODE pHead, int pos, int* pVal)
{
int i = 0;
PNODE p = pHead;
while (NULL != p && i < pos - 1)
{
p = p->pNext;
++i;
}
if (i > pos - 1 || NULL == p)
return false;
PNODE q = p->pNext;
*pVal = q->data;
//删除p节点后面的节点
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}
以上内容来源于小破站:郝斌数据结构入门