写在前面:我们在学习数据结构与算法这门课时,其中很重要的一部分就是线性结构(数组和链表),而栈和队列就是线性结构的两个重要应用,所以我整理了这部分的源代码(C语言),要是有什么地方看不懂,可以直接在评论区告诉我,我一定第一时间解答。非线性结构(树和图)有时间我再整理。
我的一点想法:相信很多朋友都学习过C语言,但谈及C语言的指针和结构体的相关内容,大家可能都比较头疼,因为这部分内容本来就十分抽象,加上大家在校期间课程又紧,所以一部分朋友应该和我一样,都是一知半解的就过去了。但在学完C语言之后,我又深入学习了C++和Java语言,发现指针和结构体十分重要,对理解许多重要知识点是必不可少的东西,所以我只能重新学习这些令人头疼的问题。
可能有人会问,C语言是面向过程的语言,而C++和Java是面向对象的语言,C语言和其他这些语言又有什么关系呢?接下来我说说我自己的理解:C++和Java里面有类的概念,可以用类实例化一个具体的对象,与此同时,我们将属性和行为封装在同一个类中,这对我们后面的编程具有十分重要的作用,而且这种方法相比C语言有了很大的进步。但对于结构体并不太理解的朋友,你也可以将结构体理解为一个类,不过结构体默认是可以公开访问的(public),而大多数面向对象语言在默认情况下只能私有访问(private)。所以说,C语言的结构体在实现复杂结构(如学生类型,有姓名、年龄、学号等属性)时也十分有用。
接下来再说说指针。相信学过C++和Java的朋友对new这个运算符并不陌生,而new的本质就是向操作系统申请一块内存,然后返回这块内存的首地址,而这个地址就存放在一个指针中。尤其在C++中,利用new创建的数据,会返回该数据对应的类型的指针。所以,理解指针是十分必要的。
除上述原因外,我觉得我们这些搞编程、搞开发的,是不是一定要会数据结构呢?答案是肯定的。数据结构简单来说就是学习数据在内存中的存储方式以及对数据的操作(算法),而在数据结构中,链表是极其重要的一种线性结构,而它的实现离不开指针(当然Java有自己的数据结构实现方式),所以我说这么多的目的就是希望大家可以好好理解指针和结构体的知识,为之后的学习做铺垫。
推荐一个非常好的数据结构的视频:哔站郝斌老师数据结构
1. 数组的定义及相关操作
#include<stdio.h>
#include <stdbool.h> //有些编译器不包含可能会报错,说不认识 bool
#include<malloc.h>
#include<stdlib.h> //包含exit函数
//定义了一个数据类型,该数据类型的名字叫做 struct Arr,含有三个成员
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); //删除指定位置的元素,并返回给用户这个被删除的值
int get(struct Arr * pArr, int pos); //获得某一位置的元素,pos从1开始
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 inversion_arr(struct Arr * pArr); //将数组倒置
int main()
{
struct Arr arr;
int val;
init_arr(&arr, 6);
show_arr(&arr);
append_arr(&arr, 5);
append_arr(&arr, 1);
append_arr(&arr, 2);
append_arr(&arr, 7);
append_arr(&arr, 6);
printf("添加元素之后的数组:\n");
show_arr(&arr);
printf("数组中第三个位置的元素为:\n");
int x = get(&arr, 3);
printf("%d\n", x);
if(delete_arr(&arr, 4, &val))
{
printf("删除成功!\n");
printf("您删除的元素是:%d\n", val);
}
else
{
printf("删除失败!\n");
}
/* insert_arr(&arr, 2, 99);
append_arr(&arr, 6);
append_arr(&arr, 7);
if(append_arr(&arr, 8))
{
printf("追加成功\n");
}
else
{
printf("追加失败\n");
}
*/
printf("删除之后的数组:\n");
show_arr(&arr);
inversion_arr(&arr);
printf("倒置之后的数组:\n");
show_arr(&arr);
sort_arr(&arr);
printf("排序之后的数组:\n");
show_arr(&arr);
return 0;
}
void init_arr(struct Arr * pArr, int length) //初始化数组
{
pArr->pBase = (int *)malloc(sizeof(int) * length);
if(pArr->pBase == NULL)
{
printf("动态分配内存失败!");
exit(-1); //终止整个程序
}
else
{
pArr->len = length;
pArr->cnt = 0;
}
return;
}
bool is_empty(struct Arr * pArr) //判断数组是否为空
{
if(pArr->cnt == 0)
{
return true;
}
else
{
return false;
}
}
bool is_full(struct Arr * pArr) //判断数组是否已满
{
if(pArr->len == pArr->cnt)
{
return true;
}
else
{
return false;
}
}
void show_arr(struct Arr * pArr) //打印数组
{
if(is_empty(pArr))
{
printf("数组为空!\n");
}
else
{
int i = 0;
for(i = 0; i < pArr->cnt; ++i)
{
printf("%d ", pArr->pBase[i]);
}
printf("\n");
}
}
bool append_arr(struct Arr * pArr, int val) //在数组末尾追加元素
{
//满时返回false
if(is_full(pArr))
{
return false;
}
//不满时追加
pArr->pBase[pArr->cnt] = val;
pArr->cnt++;
return true;
}
int get(struct Arr * pArr, int pos) //获得某一位置的元素,pos从1开始
{
if(pos < 1 || pos > pArr->cnt)
{
printf("数组中没有该位置的元素!\n");
return -1;
}
return pArr->pBase[pos - 1];
}
bool insert_arr(struct Arr * pArr, int pos, int val) //在指定位置插入元素,pos的值从1开始
{
if(is_full(pArr))
{
return false;
}
if(pos < 1 || pos > pArr->cnt + 1)
{
return false;
}
int i;
for(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;
}
int i;
*pVal = pArr->pBase[pos - 1];
for(i = pos; i < pArr->cnt; ++i)
{
pArr->pBase[i - 1] = pArr->pBase[i];
}
pArr->cnt--;
return true;
}
void inversion_arr(struct Arr * pArr) //将数组倒置
{
int i = 0;
int j = pArr->cnt - 1;
int temp;
while(i < j)
{
temp = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = temp;
++i;
--j;
}
return;
}
void sort_arr(struct Arr * pArr) //将数组排序,本程序用的是冒泡排序
{
int i, j, temp;
for(i = 0; i < pArr->cnt; ++i)
{
for(j = i + 1; j < pArr->cnt; ++j)
{
if(pArr->pBase[i] > pArr->pBase[j])
{
temp = pArr->pBase[i];
pArr->pBase[i] = pArr->pBase[j];
pArr->pBase[j] = temp;
}
}
}
}
2. 链表的定义及相关操作
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include <stdbool.h> //有些编译器不包含可能会报错,说不认识 bool
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); //求链表的长度
bool insert_list(PNODE, int, int); //在链表某个特定节点的前面插入一个指定的元素,pos从1开始
bool delete_list(PNODE, int, int *); //删除链表中某个元素
void sort_list(PNODE); //将链表排序
int main()
{
PNODE pHead = NULL;
int val; //用来存放被删除的节点的值
pHead = create_list(); //create_list()功能:创建一个非循环单链表,并将该链表的头结点地址赋给pHead
printf("初始化后的链表为:\n");
traverse_list(pHead);
/*
if(insert_list(pHead, 4, 22))
{
printf("插入成功!\n");
printf("插入该元素后的链表为:\n");
traverse_list(pHead);
}
else
{
printf("插入失败!\n");
}
*/
if(delete_list(pHead, 4, &val))
{
printf("删除成功,您删除的元素是:%d\n", val);
}
else
{
printf("删除失败!\n");
}
traverse_list(pHead);
/* if(is_empty(pHead))
{
printf("链表为空!\n");
}
else
{
printf("链表不为空!\n");
}
int len = length_list(pHead);
printf("链表的长度是:%d\n", len);
sort_list(pHead);
printf("链表排序后:\n");
traverse_list(pHead);
*/
return 0;
}
PNODE create_list(void)
{
int len; //用来存放有效节点的个数
int i;
int val; //用来临时存放用户输入的节点的值
//分配了一个不存放有效数据的头结点
PNODE pHead = (PNODE)malloc(sizeof(NODE));
if(pHead == NULL)
{
printf("分配失败,程序终止!");
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(pNew == NULL)
{
printf("分配失败,程序终止!");
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(p != NULL)
{
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
return;
}
bool is_empty(PNODE pHead)
{
if(pHead->pNext == NULL)
{
return true;
}
else
{
return false;
}
}
int length_list(PNODE pHead)
{
PNODE p = pHead->pNext;
int len = 0;
while(p != NULL)
{
++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) //类似于数组中的:a[i] > a[j]
{
t = p->data; //类似于数组中的:t = a[i]
p->data = q->data; //类似于数组中的:a[i] = a[j]
q->data = t; //类似于数组中的:a[j] = t
}
}
}
return;
}
//在pHead所指向的链表的第pos个节点的前面插入一个新的节点,该节点的值为val
//pos的值从1开始
bool insert_list(PNODE pHead, int pos, int val)
{
int i = 0;
PNODE p = pHead;
while(p != NULL && i < pos - 1)
{
p = p->pNext;
++i;
}
if(i > pos - 1 || p == NULL)
{
return false;
}
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if(pNew == NULL)
{
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(p->pNext != NULL && i < pos - 1)
{
p = p->pNext;
++i;
}
if(i > pos - 1 || p->pNext == NULL)
{
return false;
}
//删除节点p后面的节点
PNODE q = p->pNext;
*pVal = q->data;
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}
3. 栈的定义及相关操作
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include <stdbool.h> //有些编译器不包含可能会报错,说不认识 bool
typedef struct Node
{
int data;
struct Node * pNext;
}NODE, * PNODE;
typedef struct Stack
{
PNODE pTop;
PNODE pBottom;
}STACK, * PSTACK; //PSTACK等价于 struct Stack *
void init(PSTACK); //初始化栈,也就是造出一个空栈
void push(PSTACK, int); //压栈
bool is_empty(PSTACK); //判断栈是否为空
bool pop(PSTACK, int *); //出栈
void traverse(PSTACK); //遍历栈
void clear(PSTACK); //清空栈
int main()
{
STACK S; //STACK等价于struct Stack
int val; //记录出栈的那个元素的值
init(&S);
push(&S, 1);
push(&S, 2);
push(&S, 3);
push(&S, 4);
push(&S, 5);
traverse(&S);
/* clear(&S);
traverse(&S);
*/
if(pop(&S, &val))
{
printf("出栈成功!出栈的元素是:%d\n", val);
}
else
{
printf("出栈失败!");
}
traverse(&S);
return 0;
}
void init(PSTACK pS)
{
//初始化的时候,pS->pTop和pS->pBottom都指向头结点(不存放有效数据)
pS->pTop = (PNODE)malloc(sizeof(NODE));
if(pS->pTop == NULL)
{
printf("动态分配内存失败!\n");
exit(-1);
}
else
{
pS->pBottom = pS->pTop;
pS->pTop->pNext = NULL; //pS->pBottom->pNext = NULL;
}
}
void push(PSTACK pS, int val)
{
PNODE pNew = (PNODE)malloc(sizeof(NODE));
pNew->data = val;
pNew->pNext = pS->pTop; //pS->pTop不能改成 pS->pBottom
pS->pTop = pNew;
return;
}
bool is_empty(PSTACK pS)
{
if(pS->pBottom == pS->pTop)
{
return true;
}
else
{
return false;
}
}
bool pop(PSTACK pS, int * pVal)
{
if(is_empty(pS))
{
return false;
}
else
{
PNODE r = pS->pTop;
* pVal = r->data;
pS->pTop = pS->pTop->pNext; //pS->pTop = t->pNext;
free(r);
r = NULL;
return true;
}
}
void traverse(PSTACK pS)
{
PNODE p = pS->pTop;
while(p != pS->pBottom)
{
printf("%d ", p->data);
p = p->pNext;
}
printf("\n");
return;
}
void clear(PSTACK pS)
{
if(is_empty(pS))
{
return;
}
else
{
PNODE p = pS->pTop;
PNODE q = NULL;
while(p != pS->pBottom)
{
q = p->pNext;
free(p);
p = q;
}
pS->pTop = pS->pBottom;
}
}
4. 循环队列的定义及相关操作
#include<stdio.h>
#include<malloc.h>
#include<stdbool.h> //有些编译器不包含可能会报错,说不认识 bool
typedef struct Queue
{
int * pBase; //可以理解为:pBase代表数组
int front;
int rear;
}QUEUE;
void init(QUEUE *); //队列初始化,初始化的时候 front == 0 && rear == 0
bool is_full_queue(QUEUE *); //判断队列是否已满
bool is_empty_queue(QUEUE *); //判断队列是否为空
bool en_queue(QUEUE *, int); //入队
void traverse_queue(QUEUE *); //遍历队列
bool out_queue(QUEUE *, int *); //出队
int main()
{
QUEUE Q;
init(&Q);
int val; //用来存放出队的那个元素
traverse_queue(&Q);
en_queue(&Q, 1);
en_queue(&Q, 2);
en_queue(&Q, 3);
en_queue(&Q, 4);
en_queue(&Q, 5);
en_queue(&Q, 6);
en_queue(&Q, 7);
en_queue(&Q, 8);
traverse_queue(&Q);
if(out_queue(&Q, &val))
{
printf("出队成功!出队的元素是:%d\n", val);
}
else
{
printf("出队失败!\n");
}
traverse_queue(&Q);
return 0;
}
void init(QUEUE * pQ) //初始化队列,初始化的时候 front == 0 && rear == 0
{
pQ->pBase = (int *)malloc(sizeof(int) * 6);
pQ->front = 0;
pQ->rear = 0;
}
bool is_full_queue(QUEUE * pQ) //判断队列是否已满
{
if((pQ->rear + 1) % 6 == pQ->front)
{
return true;
}
else
{
return false;
}
}
bool is_empty_queue(QUEUE * pQ) //判断队列是否为空
{
if(pQ->rear == pQ->front)
{
return true;
}
else
{
return false;
}
}
bool en_queue(QUEUE * pQ, int val) //入队
{
if(is_full_queue(pQ))
{
return false;
}
else
{
pQ->pBase[pQ->rear] = val;
pQ->rear = (1 + pQ->rear) % 6;
return true;
}
}
void traverse_queue(QUEUE * pQ) //遍历队列
{
int i = pQ->front;
while(i != pQ->rear)
{
printf("%d ", pQ->pBase[i]);
i = (i + 1) % 6;
}
printf("\n");
return;
}
bool out_queue(QUEUE * pQ, int * pVal) //出队
{
if(is_empty_queue(pQ))
{
return false;
}
else
{
* pVal = pQ->pBase[pQ->front];
pQ->front = (pQ->front + 1) % 6;
return true;
}
}