《王道》数据结构代码实现与笔记

陆续更新中。。。
开始时间:2022年3月21日

引入

能力有限,文中难免有错误与不足,欢迎指正。
这篇Blog,简单的做个学王道数据结构的笔记,为了以后能快速回顾,笔记做得不好,见谅。


一、线性表

1.顺序表的顺序表示

请添加图片描述
请添加图片描述

1.1顺序表-静态数组

静态分配,大小不可改变

#include <stdio.h>
#define MaxSize 10 // 定义最大长度 

typedef struct {
    
    
	int data[MaxSize];
	int length;
}SqList;
// 初始化
void InitList(SqList& L) {
    
    
	L.length = 0;
}
// 根据位序i插入e元素
bool ListInsert(SqList& L, int i, int e) {
    
    
	if (i < 1 || i > L.length + 1) {
    
     // 是否合法 
		return false;
	}
	for (int j = L.length; j >= i; j--) {
    
    // 先向后移动
		L.data[j] = L.data[j - 1];
	}
	L.data[i - 1] = e;// 再i-1位置放置e
	L.length++; // 长度+1 
	return true;
}
// 根据位序i删除元素,返回删除的元素值e
bool ListDelete(SqList& L, int i, int& e) {
    
    
	if (i < 1 || i > L.length) {
    
    
		return false;
	}
	e = L.data[i - 1];
	for (int j = i; j < L.length; j++) {
    
    // 删除的位置后的元素往前移动
		L.data[j - 1] = L.data[j];
	}
	L.length--;
	return true;
}
// 按位序i查找元素,返回值
int GetElem(SqList& L, int i) {
    
    
	if (i < 1 || i > L.length) {
    
    
		return 0;
	}
	return L.data[i - 1];
}
// 按值e查找元素,返回位序 
int LocateElem(SqList& L, int e) {
    
    
	for (int i = 0; i < L.length; i++) {
    
    
		if (e == L.data[i]) {
    
    
			return i + 1;
		}
	}
	return 0;
}
// 遍历顺序表元素
void PrintList(SqList& L) {
    
    
	for (int i = 0; i < L.length; i++) {
    
    
		printf("%d\t", L.data[i]);
	}
	printf("\n");
}
int main() {
    
    
	int e;
	SqList L;
	InitList(L);
	ListInsert(L, 1, 1);
	ListInsert(L, 2, 3);
	ListInsert(L, 3, 4);
	ListInsert(L, 4, 5);
	printf("插入元素顺序表的元素为:\n");
	PrintList(L);
	printf("在位序2的位置插入元素:\n");
	ListInsert(L, 2, 2);// 插入2
	PrintList(L);
	if (ListDelete(L, 3, e)) {
    
    
		printf("删除位序3的元素%d成功:\n", e);
		PrintList(L);
	}
	printf("位序3的数是:%d\n", GetElem(L, 3));
	printf("数值是4的位序是:%d\n", LocateElem(L, 4));
}

请添加图片描述

1.2顺序表-动态数组

动态分配,大小可以改变。
实现的思路:在插入的时候表中元素满了,就申请新的空间,将旧的数据拷贝过来。对应IncreaseSpace()函数。

#include <stdio.h>
#include <stdlib.h>
#define InitSize 5 // 定义最大长度 
#define IncreaseSize 5// 每次改变的步长 

typedef struct {
    
    
	int* data;
	int length;
	int MaxSize;
}SqList;
// 初始化 
void InitList(SqList& L) {
    
    
	L.data = (int*)malloc(sizeof(int) * InitSize);// 初始化空间 
	L.length = 0;
	L.MaxSize = InitSize;
}
// 扩展空间,实现改变数组大小
void IncreaseSpace(SqList& L, int len) {
    
    
	printf("增加空间\n");
	int* p = L.data;// 旧数据 
	L.data = (int*)malloc(sizeof(int) * (L.MaxSize + len));// 新空间 
	for (int i = 0; i < L.length; i++) {
    
    // 拷贝旧的数据
		L.data[i] = p[i];
	}
	L.MaxSize = L.MaxSize + len;// 增加 
	free(p);// 释放原来空间 
}
// 根据位序i插入e元素
bool ListInsert(SqList& L, int i, int e) {
    
    
	if (i < 1 || i > L.length + 1) {
    
    
		return false;
	}
	// 如果满了,扩大空间
	if (L.length >= L.MaxSize) {
    
    
		IncreaseSpace(L, IncreaseSize);
	}
	// 先从前往后移动再插入
	for (int j = L.length; j >= i; j--) {
    
    
		L.data[j] = L.data[j - 1];
	}
	L.data[i - 1] = e;
	L.length++;
	return true;
}
// 根据位序i删除元素,返回删除的元素值e
bool ListDelete(SqList& L, int i, int& e) {
    
    
	if (i < 1 || i > L.length) {
    
    
		return false;
	}
	e = L.data[i - 1];
	// 直接从后往前移删除 
	for (int j = i; j < L.length; j++) {
    
    
		L.data[j - 1] = L.data[j];
	}
	L.length--;
	return true;
}
// 按位序i查找元素,返回值
int GetElem(SqList& L, int i) {
    
    
	if (i < 1 || i > L.length) {
    
    
		return 0;
	}
	return L.data[i - 1];
}
// 按值e查找元素,返回位序 
int LocateElem(SqList& L, int e) {
    
    
	for (int i = 0; i < L.length; i++) {
    
    
		if (L.data[i] == e) {
    
    
			return i + 1;
		}
	}
	return 0;
}
// 遍历顺序表元素
void PrintList(SqList& L) {
    
    
	for (int i = 0; i < L.length; i++) {
    
    
		printf("%d\t", L.data[i]);
	}
	printf("\n");
}
int main() {
    
    
	int e;
	SqList L;
	InitList(L);
	ListInsert(L, 1, 1);
	ListInsert(L, 2, 3);
	ListInsert(L, 3, 4);
	ListInsert(L, 4, 5);
	ListInsert(L, 2, 2);// 插入
	PrintList(L);
	// 此前已经插入了5个,再插入会增加空间
	printf("此前已经插入了5个\n");
	ListInsert(L, 2, 22);// 插入
	PrintList(L);
}

请添加图片描述

2.顺序表的链式表示

2.1单链表-带头结点

请添加图片描述请添加图片描述请添加图片描述
请添加图片描述请添加图片描述

#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
    
    
	int data;
	struct LNode* next;
}LNode, * LinkList;
// 单链表的建立
LinkList List_HeadInsert(LinkList& L) {
    
    // 头插法,建立逆序单链表 
	LNode* s;
	int x;
	L = (LinkList)malloc(sizeof(LNode));// 创建头结点
	L->next = NULL;						// 初始为空链表,不然会指向不确定的地方 
	scanf("%d", &x);
	while (x != 0) {
    
    
		s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		s->next = L->next;
		L->next = s;
		scanf("%d", &x);
	}
	return L;
}
LinkList List_TailInsert(LinkList& L) {
    
    // 尾插法 
	int x;
	L = (LinkList)malloc(sizeof(LNode));// 创建头结点
	LNode* s, * r = L;
	scanf("%d", &x);
	while (x != 0) {
    
    
		s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		r->next = s;
		r = s;
		scanf("%d", &x);
	}
	r->next = NULL;// 尾部的结点下一个为空 
	return L;
}
// 按位序i查找结点,头结点的位序是0,1是第一个节点 
LNode* GetElem(LinkList& L, int i) {
    
    
	// 书上写法 
//	if(i == 0){
    
    
//		return L;				// 返回头结点 
//	}
//	if(i < 1){
    
    
//		return NULL;			// 不合法 
//	}
//	int j = 1; 					// 初始化为1,因为默认位序0是头结点, 1是第一个节点 
//	LNode *p = L->next;			// 默认找到第一个结点 
//	while(p != NULL && j < i){	// 依次寻找 
//		p = p->next;
//		j++;
//	}
	// 视频写法,更简洁
	if (i < 0) {
    
    
		return NULL;
	}
	int j = 0;					// 注意:带头结点从0开始
	LNode* p = L;				// 头结点开始 
	while (p != NULL && j < i) {
    
    	// 依次寻找 
		p = p->next;
		j++;
	}
	return p;
}
// 按值e查找结点
LNode* LocateElem(LinkList& L, int e) {
    
    
	LNode* p = L->next;			// 先弄出头结点的下一个节点,也就是首元(第一个)节点 
	while (p != NULL && p->data != e) {
    
    	// 从首元(第一个)节点开始 
		p = p->next;
	}
	return p;
}
// 后插操作:在结点p后插入新节点-方法1
bool InsertNextNode(LNode* p, int e) {
    
    
	if (p == NULL) {
    
    
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));	// 创造结点 
	if (s == NULL) {
    
    
		return false;
	}
	s->data = e;
	s->next = p->next;
	p->next = s;									// 将s结点连到p之后 
	return true;
}
// 前插操作:在结点p之前插入元素e-方法2
bool InsertPriorNode(LNode* p, int e) {
    
    
	if (p == NULL) {
    
    
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));
	if (s == NULL) {
    
    
		return false;
	}
	s->next = p->next;	// 新结点s的下个节点是p的下个节点 
	p->next = s;		// 新结点s连到p之后 
	s->data = p->data;	// 将p中的元素复制到s中 
	p->data = e;		// 将p中的元素覆盖为e,完成交换 
	return true;
}
// 在位序i的位置插入结点操作
bool ListInsert(LinkList& L, int i, int e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i的前一个结点 
	return InsertNextNode(p, e);
}
// 在位序i的位置插入结点操作
bool ListInsertBefore(LinkList& L, int i, int e) {
    
    
	LNode* p = GetElem(L, i);						// 找到i结点,这里不同于上一个函数找到i-1的前一个结点 
	return InsertPriorNode(p, e);
}
// 在位序i的位置删除结点操作-方法1
bool ListDelete(LinkList& L, int i, int& e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i的前一个结点 
	if (p == NULL) {
    
    
		return false;
	}
	LNode* q = p->next;	// 令q指向被删除结点 
	p->next = q->next;	// 令q节点在链中断开 
	e = q->data;			// e为被删除结点的值 
	free(q);			// 释放被删除结点的空间 
	return true;
}
//  删除指定结点p, 不通过前驱节点操作,用交换数据那样删除,但是注意最后一个节点不能这样删除-方法2
bool DeleteZhidingLNode(LNode* p) {
    
    
	if (p == NULL) {
    
    
		return false;
	}
	LNode* q = p->next;	// 令q指向p的后继结点 
	p->data = q->data;	// 和后继结点交换数据域 
	p->next = q->next;	// 将*q结点在链中断开 
	free(q);			// 释放原后继结点的存储空间 
	return true;
}
// 在位序i的位置删除结点操作
bool ListDeleteZhiding(LinkList& L, int i, int& e) {
    
    
	LNode* p = GetElem(L, i);					// 找到i结点 
	if (p == NULL) {
    
    
		return false;
	}
	return DeleteZhidingLNode(p);
}
// 求表长
int Length(LinkList& L) {
    
    
	int j = 0;
	LNode* p = L;
	while (p->next != NULL) {
    
    // 注意是p->next  
		p = p->next;
		j++;
	}
	return j;
}
// 链表输出
void PrintLinkList(LinkList& L) {
    
    
	LNode* p = L;
	while (p->next != NULL) {
    
    
		p = p->next;
		printf("%d\t", p->data);// 放后面因为头结点是没有值的 
	}
	printf("\n");
}
int main() {
    
    
	int e;
	// 实现操作
	LinkList L;
	printf("输入值建立链表,输入0结束\n");
	List_TailInsert(L);// 尾插
	//	List_HeadInsert(L);// 头插
	PrintLinkList(L);
	// 两种插入方法测试,都是在i的位置插入节点 
	printf("在位序3的位置插入结点\n");
	ListInsert(L, 3, 33);		
	PrintLinkList(L);
	printf("在位序5的位置插入结点\n");
	ListInsertBefore(L, 5, 55); 
	PrintLinkList(L);
	// 两种删除方法测试
	printf("删除位序2的结点\n");
	ListDelete(L, 2, e);			// 通过前驱节点
	PrintLinkList(L);
	printf("删除位序4的结点\n");
	ListDeleteZhiding(L, 4, e);// 不通过前驱节点,通过交换数据方法 
	PrintLinkList(L);
}

请添加图片描述

2.2单链表-不带头结点

基本操作和不带头结点差不多,主要是在建立表的过程中对首元结点做特殊处理,并且首元结点的位序为1,查找结点应该从1开始,不同带头结点从0开始。

#include <stdio.h>
#include <stdlib.h>
typedef struct node
{
    
    
	int data;
	struct node* next;
} LNode, * LinkList;
// 链表建立
LinkList List_TailInsert(LinkList& L) {
    
    // 尾插法
	L = (LNode*)malloc(sizeof(LNode)); // 初始化一个首元节点
	int x;
	LNode* s = L, * r = NULL; // r是尾指针
	scanf("%d", &x);
	while (x != 0) {
    
    
		s->data = x;
		if (r != NULL) {
    
      // 第一次的尾指针为空
			r->next = s;// 尾指针的下一个 = 新节点
		}
		r = s;// 移动尾指针到新节点上
		scanf("%d", &x);
		s = (LNode*)malloc(sizeof(LNode));
	}
	r->next = NULL;// 尾部的结点为空 
	return L;
}
// 按位序i查找结点,1是第一个节点 
LNode* GetElem(LinkList& L, int i) {
    
    
	if (i <= 0) {
    
    
		return NULL;
	}
	int j = 1;					// // 注意:不带头结点从1开始,首元结点的位序为1
	LNode* p = L;				// 头结点开始 
	while (p != NULL && j < i) {
    
    	// 依次寻找 
		p = p->next;
		j++;
	}
	return p;
}
// 在位序i的位置插入结点操作
bool ListInsert(LinkList& L, int i, int e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i的前一个结点 
	if (p == NULL) {
    
    
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));	// 创造结点 
	if (s == NULL) {
    
    
		return false;
	}
	s->data = e;
	s->next = p->next;
	p->next = s;									// 将s结点连到p之后 
	return true;
}
// 在位序i的位置删除结点操作
bool ListDelete(LinkList& L, int i, int& e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i的前一个结点 
	if (p == NULL) {
    
    
		return false;
	}
	LNode* q = p->next;	// 令q指向被删除结点 
	p->next = q->next;	// 令q节点在链中断开 
	e = q->data;			// e为被删除结点的值 
	free(q);			// 释放被删除结点的空间 
	return true;
}
// 求表长
int Length(LinkList& L) {
    
    
	int j = 0;
	LNode* p = L;
	while (p != NULL) {
    
    // 注意是p
		p = p->next;
		j++;
	}
	return j;
}
// 链表输出
void PrintLinkList(LinkList& L) {
    
    
	LNode* p = L;
	while (p != NULL) {
    
    
		printf("%d\t", p->data);
		p = p->next;
	}
	printf("\n");
}
int main()
{
    
    
	int e;
	LinkList L;
	printf("输入值建立链表,输入0结束\n");
	List_TailInsert(L);
	printf("在位序3的位置插入结点\n");
	ListInsert(L, 3, 53);
	PrintLinkList(L);
	printf("删除位序2的结点\n");
	ListDelete(L, 2, e);
	PrintLinkList(L);
	printf("表长:%d\t", Length(L));
	return 0;
}

请添加图片描述

2.3双链表-带头结点

请添加图片描述
请添加图片描述

#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
    
    
	int data;
	struct LNode* next;// 下一个
	struct LNode* prior;// 上一个
}LNode, * LinkList;
// 双链表的建立
LinkList List_TailInsert(LinkList& L) {
    
    // 尾插法 
	int x;
	L = (LinkList)malloc(sizeof(LNode));// 创建头结点
	L->prior = NULL;				// 头结点的上一个结点为空
	LNode* s, * r = L;			// s是当前创建的结点,r是尾节点
	scanf("%d", &x);
	while (x != 0) {
    
    
		s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		r->next = s;				// 尾结点的下一个结点是刚创建的结点
		s->prior = r;			// 刚创建的结点的上一个结点是尾结点
		r = s;					// 尾节点更新为新创建的结点
		scanf("%d", &x);
	}
	r->next = NULL;				// 尾节点的下一个结点为空			
	return L;
}
// 按位序i查找结点,头结点的位序是0,1是第一个节点 
LNode* GetElem(LinkList& L, int i) {
    
    
	if (i < 0) {
    
    
		return NULL;
	}
	int j = 0;					// 注意:带头结点从0开始
	LNode* p = L;				// 头结点开始 
	while (p != NULL && j < i) {
    
    	// 依次寻找 
		p = p->next;
		j++;
	}
	return p;
}
// 按值e查找结点
LNode* LocateElem(LinkList& L, int e) {
    
    
	LNode* p = L->next;					// 先弄出头结点的下一个节点,也就是首元(第一个)节点 
	while (p != NULL && p->data != e) {
    
    	// 从首元(第一个)节点开始 
		p = p->next;
	}
	return p;
}
// 在位序i的位置插入结点操作
bool ListInsert(LinkList& L, int i, int e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i结点 
	if (p == NULL) {
    
    
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));	// 创造结点 
	if (s == NULL) {
    
    
		return false;
	}
	s->data = e;
	s->next = p->next;			
	s->prior = p;
	p->next->prior = s;
	p->next = s;									// 将s结点连到p之后,这语句必须放在最后
	return true;
}
// 在位序i的位置删除结点操作
bool ListDelete(LinkList& L, int i, int& e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i的前一个结点 
	if (p == NULL) {
    
    
		return false;
	}
	LNode* q = p->next;	// 令q指向被删除结点 
	p->next = q->next;	// 被删除结点的前一个结点的下一个结点指向被删除结点的后一个结点
	q->next->prior = p;	// 被删除结点的后一个结点的上一个结点指向被删除结点的前一个结点
	e = q->data;			// e为被删除结点的值 
	free(q);				// 释放被删除结点的空间 
	return true;
}
// 链表输出
void PrintLinkList(LinkList& L) {
    
    
	LNode* p = L;
	while (p->next != NULL) {
    
    
		p = p->next;
		printf("%d\t", p->data);// 放后面因为头结点是没有值的 
	}
	printf("\n");
}
int main() {
    
    
	int e;
	// 实现操作
	LinkList L;
	printf("输入值建立链表,输入0结束\n");
	List_TailInsert(L);  // 输入链表值,输入0结束
	PrintLinkList(L);
	printf("在位序3的位置插入结点\n");
	ListInsert(L, 3, 57);
	PrintLinkList(L);
	printf("删除位序2的结点\n");
	ListDelete(L, 2, e);
	PrintLinkList(L);
	if (L->next->next->next != NULL) {
    
    
		printf("第3个结点的前驱节点是:%d\t", L->next->next->next->prior->data);
	}
}

请添加图片描述

2.4循环单链表-带头结点

请添加图片描述
代码相对于单链表修改的地方,主要是在单链表建立时:r->next = L; // 尾部的结点指向头结点

#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
    
    
	int data;
	struct LNode* next;
}LNode, * LinkList;
// 单链表的建立
LinkList List_TailInsert(LinkList& L) {
    
    // 尾插法 
	int x;
	L = (LinkList)malloc(sizeof(LNode));// 创建头结点
	L->data = -1;						// 头结点的值初始化为-1,为了遍历代码
	LNode* s, * r = L;
	scanf("%d", &x);
	while (x != 0) {
    
    
		s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		r->next = s;
		r = s;
		scanf("%d", &x);
	}
	r->next = L;							// 尾部的结点指向头结点,这里不同
	return L;
}
// 按位序i查找结点,头结点的位序是0,1是第一个节点 
LNode* GetElem(LinkList& L, int i) {
    
    
	if (i < 0 || L == NULL) {
    
    
		return NULL;
	}
	int j = 0;					// 注意:带头结点从0开始
	LNode* p = L;				// 头结点开始 
	// 这里待优化:当查找的位序大于链表的大小则只会找到尾节点
	while (j < i) {
    
    	
		if (p->next == L)
			break;
		p = p->next;
		j++;
	}
	return p;
}
// 按值e查找结点
LNode* LocateElem(LinkList& L, int e) {
    
    
	LNode* p = L->next;					// 先弄出头结点的下一个节点,也就是首元(第一个)节点 
	while (p != NULL && p->data != e) {
    
    	// 从首元(第一个)节点开始 
		p = p->next;
	}
	return p;
}
// 在位序i的位置插入结点操作
bool ListInsert(LinkList& L, int i, int e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i的前一个结点 
	if (p == NULL) {
    
    
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));	// 创造结点 
	if (s == NULL) {
    
    
		return false;
	}
	s->data = e;
	s->next = p->next;
	p->next = s;									// 将s结点连到p之后 
	return true;
}
// 在位序i的位置删除结点操作
bool ListDelete(LinkList& L, int i, int& e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i的前一个结点 
	if (p == NULL) {
    
    
		return false;
	}
	LNode* q = p->next;	// 令q指向被删除结点 
	p->next = q->next;	// 令q节点在链中断开 
	e = q->data;			// e为被删除结点的值 
	free(q);			// 释放被删除结点的空间 
	return true;
}
// 链表输出-任意一个结点都可以循环遍历整个链表
void PrintLinkList(LinkList& startNode) {
    
    // 
	LNode* p = startNode;
	bool first = true;						// 为了让第一次进入顺利
	while (first || p != startNode) {
    
          // 如果是自己,说明到头了
		if(p->data != -1)					// 忽略头结点
			printf("%d\t", p->data); 
		p = p->next;
		first = false;
	}
	printf("\n");
}
int main() {
    
    
	int e;
	LinkList L;
	printf("输入值建立链表,输入0结束\n");
	List_TailInsert(L);
	PrintLinkList(L);
	printf("在位序3的位置插入结点\n");
	ListInsert(L, 3, 58);
	PrintLinkList(L);
	printf("删除位序2的结点\n");
	ListDelete(L, 2, e);
	PrintLinkList(L);
	printf("通过第2个结点遍历链表\n");
	PrintLinkList(L->next->next);
	printf("位序为3的结点:%d\n", GetElem(L, 3)->data);
}

请添加图片描述

2.5循环双链表-带头结点

请添加图片描述
注意,头结点的prior指向尾节点,尾节点的next指向头结点

#include <stdio.h>
#include <stdlib.h>
typedef struct LNode {
    
    
	int data;
	struct LNode* next;// 下一个
	struct LNode* prior;// 上一个
}LNode, * LinkList;
// 循环双链表的建立
LinkList List_TailInsert(LinkList& L) {
    
    // 尾插法 
	int x;
	L = (LinkList)malloc(sizeof(LNode));// 创建头结点
	L->data = -1;						// 头结点的值初始化为-1,为了遍历代码
	LNode* s, * r = L;					// s是当前创建的结点,r是尾节点
	scanf("%d", &x);
	while (x != 0) {
    
    
		s = (LNode*)malloc(sizeof(LNode));
		s->data = x;
		r->next = s;				// 尾结点的下一个结点是刚创建的结点
		s->prior = r;			// 刚创建的结点的上一个结点是尾结点
		r = s;					// 尾节点更新为新创建的结点
		scanf("%d", &x);
	}
	L->prior = r;				// 头结点的上一个结点为尾节点
	r->next = L;					// 尾节点的下一个结点为头结点		
	return L;
}
// 按位序i查找结点,头结点的位序是0,1是第一个节点 
LNode* GetElem(LinkList& L, int i) {
    
    
	if (i < 0 || L == NULL) {
    
    
		return NULL;
	}
	int j = 0;					// 注意:带头结点从0开始
	LNode* p = L;				// 头结点开始 
	// 这里待优化:当查找的位序大于链表的大小则只会找到尾节点
	while (j < i) {
    
    
		if (p->next == L)
			break;
		p = p->next;
		j++;
	}
	return p;
}
// 按值e查找结点
LNode* LocateElem(LinkList& L, int e) {
    
    
	LNode* p = L->next;					// 先弄出头结点的下一个节点,也就是首元(第一个)节点 
	while (p != NULL && p->data != e) {
    
    	// 从首元(第一个)节点开始 
		p = p->next;
	}
	return p;
}
// 在位序i的位置插入结点操作
bool ListInsert(LinkList& L, int i, int e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i结点 
	if (p == NULL) {
    
    
		return false;
	}
	LNode* s = (LNode*)malloc(sizeof(LNode));	// 创造结点 
	if (s == NULL) {
    
    
		return false;
	}
	s->data = e;
	s->next = p->next;
	s->prior = p;
	p->next->prior = s;
	p->next = s;									// 将s结点连到p之后,这语句必须放在最后
	return true;
}
// 在位序i的位置删除结点操作
bool ListDelete(LinkList& L, int i, int& e) {
    
    
	LNode* p = GetElem(L, i - 1);					// 找到i的前一个结点 
	if (p == NULL) {
    
    
		return false;
	}
	LNode* q = p->next;	// 令q指向被删除结点 
	p->next = q->next;	// 被删除结点的前一个结点的下一个结点指向被删除结点的后一个结点
	q->next->prior = p;	// 被删除结点的后一个结点的上一个结点指向被删除结点的前一个结点
	e = q->data;			// e为被删除结点的值 
	free(q);				// 释放被删除结点的空间 
	return true;
}
// 链表输出-任意一个结点都可以循环遍历整个链表
void PrintLinkList(LinkList& startNode) {
    
    
	LNode* p = startNode;
	bool first = true;						// 为了让第一次进入顺利
	while (first || p != startNode) {
    
          // 如果是自己,说明到头了
		if (p->data != -1)					// 忽略头结点
			printf("%d\t", p->data);
		p = p->next;
		first = false;
	}
	printf("\n");
}
int main() {
    
    
	int e;
	LinkList L;
	printf("输入值建立链表,输入0结束\n");
	List_TailInsert(L);
	PrintLinkList(L);
	printf("在位序3的位置插入结点\n");
	ListInsert(L, 3, 59);
	PrintLinkList(L);
	printf("删除位序2的结点\n");
	ListDelete(L, 2, e);
	PrintLinkList(L);
	printf("通过第3个结点遍历链表\n");
	PrintLinkList(L->next->next->next);
	if (L->next->next->next->next != NULL) {
    
    
		printf("第4个结点的前驱节点是%d\n", L->next->next->next->next->prior->data);
	}
}

请添加图片描述

二、栈和队列

1.栈

1.1栈的顺序存储结构

请添加图片描述
请添加图片描述

#include <stdio.h> 
#define MaxSize 50

typedef struct {
    
    
	int data[MaxSize];
	int top;
} SqStack;
// 初始化 
void InitStack(SqStack& stack) {
    
    
	stack.top = 0;
}
// 是否为空 
bool StackEmpty(SqStack& stack) {
    
    
	if (stack.top == 0) {
    
    
		return true;
	}
	return false;
}
// 入栈 
bool Push(SqStack& stack, int e) {
    
    
	if (stack.top == MaxSize) {
    
    // 是否满了 
		return false;
	}
	stack.data[stack.top] = e;
	stack.top++;
	return true;
}
// 出栈 
bool Pop(SqStack& stack, int& e) {
    
    
	if (stack.top == 0) {
    
    
		return false;
	}
	stack.top--;
	e = stack.data[stack.top];
	return true;
}
// 获取栈顶 
bool GetTop(SqStack& stack, int& e) {
    
    
	if (stack.top == 0) {
    
    
		return false;
	}
	e = stack.data[stack.top - 1];
	return true;
}
// 遍历
void PrintStack(SqStack& stack) {
    
    
	for (int i = 0; i < stack.top; i++) {
    
    
		printf("%d\t", stack.data[i]);
	}
	printf("\n");
}
int main()
{
    
    
	int e;
	bool b;
	SqStack stack;
	InitStack(stack);// 初始化
	printf("栈是否为空:%d\n", StackEmpty(stack));
	Push(stack, 1);
	Push(stack, 2);
	Push(stack, 3);
	Push(stack, 4);
	printf("元素压入栈后:\n");
	PrintStack(stack);
	b = Pop(stack, e);
	if (b) printf("出栈元素:%d\n", e); 
	b = GetTop(stack, e);
	if (b) printf("栈顶元素是:%d\n", e);
	Push(stack, 7);
	printf("元素7压入栈后:\n");
	PrintStack(stack);
	return 0;
}

请添加图片描述

1.2栈的链式存储结构

请添加图片描述
不带头结点实现如下

#include <stdio.h> 
#include <stdlib.h>
// 不带头结点
typedef struct Linknode {
    
    
	int data;
	struct Linknode* next;
}Linknode, * LiStack;
// 后插法插入,在链栈首处插入一个节点 
bool InsertNextNode(LiStack& L, int e) {
    
    
	Linknode* s = (Linknode*)malloc(sizeof(Linknode));
	s->data = e;
	if (L == NULL) {
    
    
		L = s;	// 第一个节点 
		s->next = NULL;// 下一个初始化为空 !!! 
	}
	else {
    
    
		s->next = L;
		L = s;
	}
	return true;
}
// 头插法建立链表
LiStack LiStack_HeadInsert(LiStack& L) {
    
    
	int x = 1;
	scanf("%d", &x);
	while (x != 0) {
    
    
		InsertNextNode(L, x);
		scanf("%d", &x);
	}
	return L;
}
// 入栈
bool Push(LiStack& L, int e) {
    
    
	return InsertNextNode(L, e);
}
// 出栈
bool Pop(LiStack& L, int& e) {
    
    
	if (L == NULL) {
    
    
		return false;
	}
	Linknode* s = L;
	e = s->data;
	L = s->next;
	free(s);
	return true;
}
// 是否为空
bool LiStackEmpty(LiStack& L) {
    
    
	if (L == NULL) {
    
    
		return true;
	}
	return false;
}
// 打印
void PrintLiStack(LiStack& L) {
    
    
	if (L == NULL) {
    
    
		return;
	}
	Linknode* p = L;
	while (p != NULL) {
    
    
		printf("%d\t", p->data);
		p = p->next;
	}
	printf("\n");
}
int main()
{
    
    
	int e;
	bool b;
	LiStack L = NULL;
	b = LiStackEmpty(L);
	if (b) printf("栈为空\n");
	printf("输入值建立栈,输入0结束\n");
	LiStack_HeadInsert(L);
	PrintLiStack(L);
	Push(L, 11);
	Push(L, 13);
	Push(L, 16);
	printf("元素压入栈后:\n");
	PrintLiStack(L);
	b = Pop(L, e);
	printf("出栈元素:%d\n", e);
	PrintLiStack(L);
	b = Pop(L, e);
	printf("出栈元素:%d\n", e);
	PrintLiStack(L);
	
	return 0;
}

请添加图片描述

2.队列

请添加图片描述

2.1队列的顺序存储

请添加图片描述

#include <stdio.h> 
#include <stdlib.h>
#define MaxSize 5
typedef struct {
    
    
	int data[MaxSize];
	int front, rear;
}SqQueue;
// 初始化 
void InitQueue(SqQueue& Q) {
    
    
	Q.front = Q.rear = 0;
}
// 判断为空
bool QueueEmpty(SqQueue& Q) {
    
    
	if (Q.front == Q.rear) {
    
    
		return true;
	}
	return false;
}
// 入队 - 只能在队尾插入 
bool EnQueue(SqQueue& Q, int e) {
    
    
	if (Q.rear == MaxSize) {
    
    // 判满 
		return false;
	}
	Q.data[Q.rear] = e;
	Q.rear = Q.rear + 1;
	return true;
}
// 出队 - 只能在队头出 
bool DeQueue(SqQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear) {
    
    // 判空 
		return false;
	}
	e = Q.data[Q.front];
	Q.front = Q.front + 1;
	return true;
}
// 读队头元素
bool GetHead(SqQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear) {
    
    
		return false;
	}
	e = Q.data[Q.front];  // 注意不同于出队操作,front不+1
	return true;
}
int GetSize(SqQueue& Q) {
    
    
	return Q.rear - Q.front;
}
// 打印
void PrintLiStack(SqQueue& S) {
    
    
	for (int i = S.front; i != S.rear; i++) {
    
    
		printf("%d\t", S.data[i]);
	}
	printf("\n");
}
int main()
{
    
    
	int e;
	bool b;
	SqQueue Q;
	InitQueue(Q);
	if (QueueEmpty(Q)) printf("队列为空\n");
	EnQueue(Q, 1);
	printf("元素1入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 3);
	printf("元素3入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 5);
	printf("元素5入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 7);
	printf("元素7入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 9);
	printf("元素9入队后,队列大小:%d\n", GetSize(Q));
	PrintLiStack(Q);

	b = EnQueue(Q, 10);
	if (!b) printf("队列已满,插入10失败!\n");

	DeQueue(Q, e);
	printf("队头元素 %d 出队\n", e);

	b = EnQueue(Q, 10);
	if (!b) {
    
    
		printf("队列已满,插入10失败!,队列大小: % d\n", GetSize(Q));
		printf("-----说明队列假溢出!-----\n");
		PrintLiStack(Q);
	}
	DeQueue(Q, e);DeQueue(Q, e);DeQueue(Q, e);DeQueue(Q, e);
	if (QueueEmpty(Q)) {
    
    
		printf("所有元素出队,队列为空\n");
	}
	return 0;
}

请添加图片描述

2.1.0循环队列-顺序存储

请添加图片描述
请添加图片描述
以下三种方法入队和出队后都需%size
Q.front = (Q.front + 1) % MaxSize;
Q.rear = (Q.rear + 1) % MaxSize;

2.1.1牺牲一个单元

初始化:队头队尾初始为0,队头指向在队尾下一个位置,这样通过牺牲这个单元而区分开队列判空和判满。
判空:front == rear
判满:(rear + 1)% MAXSIZE == front

#include <stdio.h> 
#include <stdlib.h>
#define MaxSize 5
typedef struct {
    
    
	int data[MaxSize];
	int front, rear;
}SqQueue;
// 初始化 
void InitQueue(SqQueue& Q) {
    
    
	Q.front = Q.rear = 0;
}
// 判断为空
bool QueueEmpty(SqQueue& Q) {
    
    
	if (Q.front == Q.rear) {
    
    
		return true;
	}
	return false;
}
// 入队 - 只能在队尾插入 
bool EnQueue(SqQueue& Q, int e) {
    
    
	if ((Q.rear + 1) % MaxSize == Q.front) {
    
    // 判满 
		return false;
	}
	Q.data[Q.rear] = e;
	Q.rear = (Q.rear + 1) % MaxSize;
	return true;
}
// 出队 - 只能在队头出 
bool DeQueue(SqQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear) {
    
    // 判空 
		return false;
	}
	e = Q.data[Q.front];
	Q.front = (Q.front + 1) % MaxSize;
	return true;
}
// 读队头元素
bool GetHead(SqQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear) {
    
    
		return false;
	}
	e = Q.data[Q.front];
	return true;
}
int GetSize(SqQueue& Q) {
    
    
	return (Q.rear - Q.front + MaxSize) % MaxSize;
}
// 打印
void PrintSqQueue(SqQueue& S) {
    
    
	for (int i = S.front; i != S.rear; i = (i + 1) % MaxSize) {
    
    
		printf("%d\t", S.data[i]);
	}
	printf("\n");
}
int main()
{
    
    
	int e;
	bool b;
	SqQueue Q;
	InitQueue(Q);
	if (QueueEmpty(Q)) printf("队列为空\n");
	EnQueue(Q, 1);
	printf("元素1入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 3);
	printf("元素3入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 5);
	printf("元素5入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 9);
	printf("元素9入队后,队列大小:%d\n", GetSize(Q));
	PrintSqQueue(Q);

	b = EnQueue(Q, 10);
	if (!b) printf("因为牺牲了一个单元,队列已满, 插入10失败!\n");
	DeQueue(Q, e);
	printf("队头元素 %d 出队\n", e);
	EnQueue(Q, 10);
	printf("元素10入队后,队列大小:%d\n", GetSize(Q));
	PrintSqQueue(Q);

	DeQueue(Q, e); 
	printf("队头元素 %d 出队,队列大小:%d\n", e, GetSize(Q));
	PrintSqQueue(Q);

	EnQueue(Q, 12);
	printf("元素12入队后,队列大小:%d\n", GetSize(Q));
	PrintSqQueue(Q);

	DeQueue(Q, e); DeQueue(Q, e); DeQueue(Q, e);
	if (QueueEmpty(Q)) {
    
    
		printf("所有元素出队,队列为空\n");
	}
	return 0;
}

请添加图片描述

2.1.2Size变量

初始化:队头队尾初始为0

#include <stdio.h> 
#include <stdlib.h>
#define MaxSize 5
typedef struct {
    
    
	int data[MaxSize];
	int front, rear;
	int size;
}SqQueue;
// 初始化 
void InitQueue(SqQueue& Q) {
    
    
	Q.front = Q.rear = 0;
	Q.size = 0;
}
// 判断为空
bool QueueEmpty(SqQueue& Q) {
    
    
	if (Q.front == Q.rear && Q.size != MaxSize) {
    
    
		return true;
	}
	return false;
}
// 入队 - 只能在队尾插入 
bool EnQueue(SqQueue& Q, int e) {
    
    
	if (Q.size == MaxSize) {
    
    // 判满 
		return false;
	}
	Q.data[Q.rear] = e;
	Q.rear = (Q.rear + 1) % MaxSize;
	Q.size++;
	return true;
}
// 出队 - 只能在队头出 
bool DeQueue(SqQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear && Q.size != MaxSize) {
    
    // 判空 
		return false;
	}
	e = Q.data[Q.front];
	Q.front = (Q.front + 1) % MaxSize;
	Q.size--;
	return true;
}
// 读队头元素
bool GetHead(SqQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear && Q.size != MaxSize) {
    
    
		return false;
	}
	e = Q.data[Q.front];
	return true;
}
int GetSize(SqQueue& Q) {
    
    
	return Q.size;
}
// 打印
void PrintSqQueue(SqQueue& Q) {
    
    
	int ct = 0;
	for (int i = Q.front; ct < Q.size; i = (i + 1) % MaxSize) {
    
    
		printf("%d\t", Q.data[i]);
		ct++;
	}
	printf("\n");
}
int main()
{
    
    
	int e;
	bool b;
	SqQueue Q;
	InitQueue(Q);
	if (QueueEmpty(Q)) printf("队列为空\n");
	EnQueue(Q, 1);
	printf("元素1入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 3);
	printf("元素3入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 5);
	printf("元素5入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 8);
	printf("元素8入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 9);
	printf("元素9入队后,队列大小:%d\n", GetSize(Q));
	PrintSqQueue(Q);

	b = EnQueue(Q, 10);
	if (!b) printf("队列已满, 插入10失败!\n");
	DeQueue(Q, e);
	printf("队头元素 %d 出队\n", e);
	EnQueue(Q, 10);
	printf("元素10入队后,队列大小:%d\n", GetSize(Q));
	PrintSqQueue(Q);

	DeQueue(Q, e);
	printf("队头元素 %d 出队,队列大小:%d\n", e, GetSize(Q));
	PrintSqQueue(Q);

	EnQueue(Q, 12);
	printf("元素12入队后,队列大小:%d\n", GetSize(Q));
	PrintSqQueue(Q);

	DeQueue(Q, e); DeQueue(Q, e); DeQueue(Q, e);
	if (QueueEmpty(Q)) {
    
    
		printf("所有元素出队,队列为空\n");
	}
	return 0;
}

请添加图片描述

2.1.3Tag变量

初始化:队头队尾初始为0

#include <stdio.h> 
#include <stdlib.h>
#define MaxSize 5
typedef struct {
    
    
	int data[MaxSize];
	int front, rear;
	int tag;// 0为删除,默认。1为增加  
}SqQueue;
// 初始化 
void InitQueue(SqQueue& Q) {
    
    
	Q.front = Q.rear = 0;
	Q.tag = 0;
}
// 判断为空
bool QueueEmpty(SqQueue& Q) {
    
    
	if (Q.front == Q.rear && Q.tag == 0) {
    
    
		return true;
	}
	return false;
}
// 入队 - 只能在队尾插入 
bool EnQueue(SqQueue& Q, int e) {
    
    
	if (Q.front == Q.rear && Q.tag == 1) {
    
    // 判满 
		return false;
	}
	Q.data[Q.rear] = e;
	Q.rear = (Q.rear + 1) % MaxSize;
	Q.tag = 1;
	return true;
}
// 出队 - 只能在队头出 
bool DeQueue(SqQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear && Q.tag == 0) {
    
    // 判空 
		return false;
	}
	e = Q.data[Q.front];
	Q.front = (Q.front + 1) % MaxSize;
	Q.tag = 0;
	return true;
}
// 读队头元素
bool GetHead(SqQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear && Q.tag == 0) {
    
    // 判空
		return false;
	}
	e = Q.data[Q.front];
	return true;
}
int GetSize(SqQueue& Q) {
    
    
	if (Q.front == Q.rear && Q.tag == 0) {
    
    // 空 
		return 0;
	}
	if (Q.front == Q.rear && Q.tag == 1) {
    
    // 满了 
		return MaxSize;
	}
	return (Q.rear - Q.front + MaxSize) % MaxSize; // 没满 
}
// 打印
void PrintSqQueue(SqQueue& Q) {
    
    
	if (Q.front == Q.rear && Q.tag == 0) {
    
    // 为空 
		return;
	}
	if (Q.front == Q.rear && Q.tag == 1) {
    
    // 满 
		int ct = 0;
		for (int i = Q.front; ct < MaxSize; i = (i + 1) % MaxSize) {
    
    
			printf("%d\t", Q.data[i]);
			ct++;
		}
	}
	else {
    
    // 不满 
		for (int i = Q.front; i != Q.rear; i = (i + 1) % MaxSize) {
    
    
			printf("%d\t", Q.data[i]);
		}
	}
	// 是否输出 
	printf("\n");
}
int main()
{
    
    
	int e;
	bool b;
	SqQueue Q;
	InitQueue(Q);
	if (QueueEmpty(Q)) printf("队列为空\n");
	EnQueue(Q, 1);
	printf("元素1入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 3);
	printf("元素3入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 5);
	printf("元素5入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 8);
	printf("元素8入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 9);
	printf("元素9入队后,队列大小:%d\n", GetSize(Q));
	PrintSqQueue(Q);

	b = EnQueue(Q, 10);
	if (!b) printf("队列已满, 插入10失败!\n");
	DeQueue(Q, e);
	printf("队头元素 %d 出队\n", e);
	EnQueue(Q, 10);
	printf("元素10入队后,队列大小:%d\n", GetSize(Q));
	PrintSqQueue(Q);

	DeQueue(Q, e);
	printf("队头元素 %d 出队,队列大小:%d\n", e, GetSize(Q));
	PrintSqQueue(Q);

	EnQueue(Q, 12);
	printf("元素12入队后,队列大小:%d\n", GetSize(Q));
	PrintSqQueue(Q);

	DeQueue(Q, e); DeQueue(Q, e); DeQueue(Q, e);
	if (QueueEmpty(Q)) {
    
    
		printf("所有元素出队,队列为空\n");
	}
	return 0;
}

请添加图片描述

2.2队列的链式存储

请添加图片描述
队头是头结点,队尾是是尾结点
对应入队尾指针指向新结点,出队只需让头指针指向下一个即可。

带头结点实现如下

#include <stdio.h> 
#include <stdlib.h>
// 带头结点
typedef struct LinkNode {
    
    
	int data;
	struct LinkNode* next;
}LinkNode;

typedef struct {
    
    
	LinkNode* front, * rear;
}LinkQueue;
// 初始化 
void InitQueue(LinkQueue& Q) {
    
    
	// 头结点
	LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
	Q.front = Q.rear = p;	// 初始化头和尾同指向头结点 
	Q.rear->next = NULL;		// 头结点的下一个置为空
}
// 判断为空
bool QueueEmpty(LinkQueue& Q) {
    
    
	return Q.front == Q.rear;
}
// 入队 - 只能在队尾插入 
bool EnQueue(LinkQueue& Q, int e) {
    
    
	LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
	p->data = e;
	p->next = NULL;// 注意新结点是要放到末尾的,下一个要置为空,等价 p->next = Q.rear->next
	Q.rear->next = p;
	Q.rear = p;// 更新尾结点为新结点
	return true;
}
// 出队 - 只能在队头出 
bool DeQueue(LinkQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear) {
    
    // 判空 
		return false;
	}
	LinkNode* p = Q.front->next;	// 记录首元结点
	e = p->data;
	Q.front->next = p->next;
	// 注意!当删除后只有一个头结点的时候,尾指针得修改,因为尾指针指向的结点会被删除,会导致尾指针指向位置丢失
	if (Q.front->next == NULL || Q.rear == p) {
    
    // 是否只有一个节点
		Q.front = Q.rear;						// 两个指针同指向头结点,代表为空
	}
	free(p);
	return true;
}
// 读队头元素
bool GetHead(LinkQueue& Q, int& e) {
    
    
	if (Q.front == Q.rear) {
    
    // 判空 
		return false;
	}
	e = Q.front->next->data;
	return true;
}
int GetSize(LinkQueue& Q) {
    
    
	int size = 0;
	LinkNode* p = Q.front;
	while (p->next != NULL) {
    
    
		p = p->next;
		size++;
	}
	return size;
}
// 打印
void PrintLiStack(LinkQueue& Q) {
    
    
	LinkNode* p = Q.front;
	while (p->next != NULL) {
    
    
		p = p->next;
		printf("%d\t", p->data);
	}
	printf("\n");
}
int main()
{
    
    
	int e;
	bool b;
	LinkQueue Q;
	InitQueue(Q);
	if (QueueEmpty(Q)) printf("队列为空\n");
	EnQueue(Q, 1);
	printf("元素1入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 6);
	printf("元素6入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 7);
	printf("元素7入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 9);
	printf("元素9入队后,队列大小:%d\n", GetSize(Q));
	EnQueue(Q, 11);
	printf("元素11入队后,队列大小:%d\n", GetSize(Q));
	PrintLiStack(Q);

	DeQueue(Q, e);
	printf("队头元素 %d 出队\n", e);
	PrintLiStack(Q);
	DeQueue(Q, e);
	printf("队头元素 %d 出队\n", e);
	PrintLiStack(Q);

	EnQueue(Q, 15);
	printf("元素15入队后,队列大小:%d\n", GetSize(Q));
	PrintLiStack(Q);

	DeQueue(Q, e); DeQueue(Q, e); DeQueue(Q, e); DeQueue(Q, e);
	if (QueueEmpty(Q)) {
    
    
		printf("所有元素出队,队列为空\n");
	}
	return 0;
}

请添加图片描述

3.栈和队列的应用

3.1栈的应用

3.1.1括号匹配

请添加图片描述

#include <stdio.h> 
#include <string.h>
#include <stdlib.h>

typedef struct Linknode {
    
    
	char data;
	struct Linknode* next;
}Linknode, * LiStack;
// 后插法插入,在链栈首处插入一个节点 
bool InsertNextNode(LiStack& L, char e) {
    
    
	Linknode* s = (Linknode*)malloc(sizeof(Linknode));
	s->data = e;
	if (L == NULL) {
    
    
		L = s;	// 第一个节点 
		s->next = NULL;// 下一个初始化为空 !!! 
	}
	else {
    
    
		s->next = L;
		L = s;
	}
	return true;
}
// 入栈
bool Push(LiStack& L, char e) {
    
    
	return InsertNextNode(L, e);
}
// 出栈
bool Pop(LiStack& L, char& e) {
    
    
	if (L == NULL) {
    
    
		return false;
	}
	Linknode* s = L;
	e = s->data;
	L = s->next;
	free(s);
	return true;
}
// 是否为空
bool LiStackEmpty(LiStack& L) {
    
    
	if (L == NULL) {
    
    
		return true;
	}
	return false;
}
// 打印
void PrintLiStack(LiStack& L) {
    
    
	if (L == NULL) {
    
    
		return;
	}
	Linknode* p = L;
	while (p != NULL) {
    
    
		printf("%d\t", p->data);
		p = p->next;
	}
	printf("\n");
}
// 括号匹配
int MatchKuohao(LiStack& L, const char* s, int len) {
    
    
	char c = 0;
	for (int i = 0; i < len; i++) {
    
    
		c = s[i];
		// 1.判断是否为左括号 是就入栈 
		if (c == '(' || c == '{' || c == '[') {
    
    
			Push(L, c);
		}
		else {
    
    
			// 2.是右括号,取出栈顶括号与之匹配
			if (LiStackEmpty(L)) {
    
     // 不存在左括号与之匹配,代表失败 
				return 1;
			}
			Pop(L, c);
			// 判断是否匹配 
			if (s[i] == ')' && c != '(') {
    
    
				return 2;
			}
			if (s[i] == '}' && c != '{') {
    
    
				return 2;
			}
			if (s[i] == ']' && c != '[') {
    
    
				return 2;
			}
		}
	}
	// 3.在判断栈是否有左括号未匹配的
	if (!LiStackEmpty(L)) {
    
    
		return 3;
	}
	return 0;
}
int main()
{
    
    
	int e;
	LiStack L = NULL;
	// 括号匹配开始
	const char* s = "({()})";
	int b = MatchKuohao(L, s, strlen(s));
	printf("%s", s);
	if (b == 0) {
    
    
		printf("括号匹配成功\n");
	}
	else if (b == 1) {
    
    
		printf("括号匹配失败, 右括号多了\n");
	}
	else if (b == 2) {
    
    
		printf("括号匹配失败,左右括号不匹配\n");
	}
	else if (b == 3) {
    
    
		printf("括号匹配失败, 左括号多了\n");
	}
	return 0;
}

请添加图片描述
请添加图片描述

3.1.2中缀转后缀表达式与后缀表达式计算

中缀转后缀文字描述
在这里插入图片描述
例题
在这里插入图片描述

后缀表达式计算文字描述
在这里插入图片描述
例题
请添加图片描述
代码一起

#include <string.h>
#include <iostream>
#include <stack> 
using namespace std;
// 对比运算符(当c1 > c2 返回1, 当 c1 < c2 返回-1 )
int CompareCal(char c1, char c2) {
    
    // c1,c2 是运算符,假定只有+-*/
	switch (c1) {
    
    
	case '+':case '-':
		if (c2 == '*' || c2 == '/')// “+ -”小于“* /”
			return -1;
	}
	return 1;// 默认大于
}
int Cal(int val1, char op, int val2) {
    
    
	switch (op) {
    
    
	case '+':
		return val1 + val2;
	case '-':
		return val1 - val2;
	case '*':
		return val1 * val2;
	case '/':
		return val1 / val2;
	}
	return 0;
}
// 1.中缀转后缀表达式生成算法 
string GenerateHou(stack<char>& opstack, string& s) {
    
    
	string hous;
	char curc, zhanc;// 当前扫描的字符和栈中的字符 
	for (int i = 0; i < s.length(); i++) {
    
    
		curc = s[i];
		// 1.是否数字 ,是就加入后缀表达式中 
		if (isdigit(curc)) {
    
    
			hous = hous + curc;
		}
		else {
    
    
			// 2.不是数字,判断是否界限符
			if (curc == '(') {
    
    // 加入到栈中 
				opstack.push(curc);
			}
			else if (curc == ')') {
    
    // 弹出栈的元素加到后缀中 
				if (!opstack.empty()) {
    
    
					zhanc = opstack.top();
					opstack.pop();
					while (zhanc != '(') {
    
    // 弹出栈中的运算符
						hous = hous + zhanc;
						// 再次出运算符 
						zhanc = opstack.top();
						opstack.pop();
					}
				}
			}
			else {
    
    
				// 重点:3. 是运算符,依次弹出栈中运算符比当前运算符优先级更高和相等的,加入后缀表达式 ,当前运算符入栈 。
				/*
				三种情况,不弹栈中运算符,并将当前运算符加入栈中
				1.栈空
				2.栈顶运算符是'('
				3.当前运算符比栈顶运算符优先级高
				*/
				while (!opstack.empty()) {
    
    
					zhanc = opstack.top();
					if (zhanc == '(') {
    
    
						break;
					}
					if (CompareCal(curc, zhanc) == -1) {
    
     // 当前运算符比栈中运算符优先级低=栈中运算符比当前运算符优先级高,就把栈运算符加入后缀表达式
						// 栈顶运算符优先级高出栈
						zhanc = opstack.top();
						opstack.pop();
						hous = hous + zhanc;
					}
					else {
    
    
						break;
					}
				}
				opstack.push(curc);
			}
		}
	}
	// 把栈中剩下的放入后缀表达式中
	while (!opstack.empty()) {
    
    
		zhanc = opstack.top();
		opstack.pop();
		hous = hous + zhanc;
	}
	return hous;
}
// 2.后缀表达式计算 
int CalHou(stack<int>& distack, string& s) {
    
    
	int leftv, rightv;// 左值右值 
	int sum = 0;
	char curc;
	for (int i = 0; i < s.length(); i++) {
    
    
		curc = s[i];
		// 1.是操作数直接压入栈 
		if (isdigit(curc)) {
    
    
			distack.push(curc - '0');
		}
		else {
    
    
			// 2.是运算符,进行弹出栈顶两个元素计算,记住算完后的值再入栈。注意第一次弹出的是右值,第二次才是左值
			rightv = distack.top();
			distack.pop();

			leftv = distack.top();
			distack.pop();
			sum = Cal(leftv, curc, rightv);
			distack.push(sum);
		}
	}
	return sum;
}
int main()
{
    
    
	// 程序小缺点:如果数值大于等于10就会算错。 
	
	// 1.生成后缀表达式
	stack<char> opstack0;
	string s1 = "1+4*3+2";
	string s2 = GenerateHou(opstack0, s1);
	cout << "中缀表达式" << s1 << "转换后缀表达式为:" << s2 << endl;
	// 2.计算后缀表达式
	stack<int> distack0;
	int val = CalHou(distack0, s2);
	cout << "计算后缀表达式" << s2 << "得:" << val << endl;
	return 0;
}

请添加图片描述

3.1.3中缀算术表达式计算

文字描述
在这里插入图片描述
由3.1.2中缀转后缀表达式修改而成,王道代码风格,比较冗余

#include <string.h>
#include <iostream>
#include <stack> 
using namespace std;
// 辅助函数
// 对比运算符(当c1 > c2 返回1, 当 c1 < c2 返回-1 )
int CompareCal(char c1, char c2) {
    
    // c1,c2 是运算符,假定只有+-*/
	switch (c1) {
    
    
	case '+':case '-':
		if (c2 == '*' || c2 == '/')// “+ -”小于“* /”
			return -1;
	}
	return 1;
}
// 计算
int Cal(int val1, char op, int val2) {
    
    
	switch (op) {
    
    
	case '+':
		return val1 + val2;
	case '-':
		return val1 - val2;
	case '*':
		return val1 * val2;
	case '/':
		return val1 / val2;
	}
	return 0;
}
// 中缀转后缀表达式边生成和边计算算法 
int CalExpression(stack<int>& distack, stack<char>& opstack, string& s) {
    
    
	char curc, zhanc;// 当前扫描的字符和栈中的字符 
	int leftv, rightv, sum;
	for (int i = 0; i < s.length(); i++) {
    
    
		curc = s[i];
		// 1.是否数字 ,是就加入后缀表达式中 
		if (isdigit(curc)) {
    
    
			distack.push(curc - '0');
		}
		else {
    
    
			// 2.不是数字,判断是否界限符
			if (curc == '(') {
    
    // 加入到栈中 
				opstack.push(curc);
			}
			else if (curc == ')') {
    
    // 弹出栈的元素加到后缀中 
				if (!opstack.empty()) {
    
    
					zhanc = opstack.top();
					opstack.pop();
					while (zhanc != '(') {
    
    
						// 相对于后缀表达式生成代码,修改的地方,得计算
						rightv = distack.top();
						distack.pop();

						leftv = distack.top();
						distack.pop();

						sum = Cal(leftv, zhanc, rightv);
						// 数值入栈 
						distack.push(sum);
						// 再次出运算符 
						zhanc = opstack.top();
						opstack.pop();
					}
				}
			}
			else {
    
    
				// 重点:3. 是运算符,依次弹出栈中运算符比当前运算符优先级更高和相等的,加入后缀表达式 
				/*
				三种情况,不弹栈中运算符,并将当前运算符加入栈中
				1.栈空
				2.栈顶运算符是'('
				3.当前运算符比栈顶运算符优先级高
				*/
				while (!opstack.empty()) {
    
    
					zhanc = opstack.top();// 注意这里 
					if (zhanc == '(') {
    
    
						break;
					}
					if (CompareCal(curc, zhanc) == -1) {
    
     // 当前运算符比栈中运算符优先级低=栈中运算符比当前运算符优先级高,就把栈运算符加入后缀表达式
						// 栈顶运算符优先级高就计算
						// 相对于后缀表达式生成代码 修改的地方 
						zhanc = opstack.top();
						opstack.pop();

						rightv = distack.top();
						distack.pop();

						leftv = distack.top();
						distack.pop();

						sum = Cal(leftv, zhanc, rightv);
						// 数值入栈 
						distack.push(sum);
					}
					else {
    
    
						break;
					}
				}
				opstack.push(curc);
			}
		}
	}
	// 把栈中剩下的计算 
	while (!opstack.empty()) {
    
    
		// 相对于后缀表达式生成代码 修改的地方 
		zhanc = opstack.top();
		opstack.pop();

		rightv = distack.top();
		distack.pop();

		leftv = distack.top();
		distack.pop();

		sum = Cal(leftv, zhanc, rightv);
		// 数值入栈 
		distack.push(sum);
	}
	return distack.top();
}
int main()
{
    
    
	// 程序小缺点:如果数值大于等于10就会算错。
	 
	// 计算算术表达式
	// 需要两个栈,一个运算符栈,一个操作数栈
	stack<int> distack;
	stack<char> opstack;
	string s3 = "1*(4+3)+2";

	int val2 = CalExpression(distack, opstack, s3);
	cout << "计算中缀算术表达式" << s3 << "得:" << val2 << endl;
	return 0;
}


在这里插入图片描述

接下来的是看严蔚敏数据结构书,代码相对比较简洁,但运算符比较代码比较冗余:

// 泛型链栈。泛型是为了让题目:算术表达式不用另写一个文件代码
#include <iostream>
#include <string>
#include <stack> 
using namespace std;
// 辅助函数
// 1.比较两个运算符优先,(当c1 > c2 返回1, 当 c1 < c2 返回-1 , 当c1 = c2 返回0)
int CompareCal(char c1, char c2) {
    
     // 只考虑 +-*/()#; 任何运算符 代表+-*/()#
	// # 是开始与 结束符合
	if (c1 == '#' && c2 == '#') {
    
    
		return 0;
	}
	if (c1 == '#') return -1;// c1“#”都小于于除了“#”的任何运算符
	if (c2 == '#') return 1;// c1永远优先 c2为#时
	if (c1 == '(' && c2 == ')') {
    
    
		return 0;
	}
	if (c1 == '(' || c2 == '(') {
    
    // “(”都小于于除了“)”的任何运算符
		return -1;
	}
	if (c1 == ')' || c2 == ')')// “)”都大于除了“(”的任何运算符
		return 1;

	switch (c1)
	{
    
    
	case '+':case '-':
		if (c2 == '*' || c2 == '/')// “+ -”小于“* /”
			return -1;
	}
	return 1; // 默认小于
}
// 2.计算
int Cal(int val1, char op, int val2) {
    
    
	switch (op) {
    
    
	case '+':
		return val1 + val2;
	case '-':
		return val1 - val2;
	case '*':
		return val1 * val2;
	case '/':
		return val1 / val2;
	}
	return 0;
}
// 计算算术表达式代码-来自严蔚敏书
int CalExpression(stack<int>& valstack, stack<char>& opstack, string& s) {
    
     // opstack是运算符栈,valstack是操作数栈
	s.append("#");									// 添加结束标志
	opstack.push('#');								// 运算符栈结束标志
	int a, b;										// a是左操作数,b是右操作数
	char curc, zhanc;								// 当前扫描的字符和栈中的字符 
	int i = 0;										// i下标为0开始
	while (s[i] != '#' || opstack.top() != '#') {
    
    // 表达式没有扫描完毕或操作栈运算符还没计算完
		curc = s[i];
		if (isdigit(curc)) {
    
    							// ch是数值就放入数值栈
			valstack.push(int(curc - '0'));
			i++;										// 扫描下一个字符
			continue;
		}
		switch (CompareCal(opstack.top(), curc))		// 注意是栈顶的运算符与当前的运算符对比
		{
    
    
		case -1:										// <,当前运算符入栈
			opstack.push(curc);
			i++;										// 扫描下一个字符
			break;
		case 1:										// >,取出数值栈中存储的两个值根据运算符栈的运算符运算。
			zhanc = opstack.top();
			opstack.pop();

			b = valstack.top();						// b是右操作数
			valstack.pop();

			a = valstack.top();
			valstack.pop();							// a是左操作数
			valstack.push(Cal(a, zhanc, b));
			break;
		case 0:										// =, 相等
			opstack.pop();
			i++;										// 扫描下一个字符
			break;
		}
	}
	return valstack.top();
}
int main() {
    
    
	// 程序小缺点:如果数值大于等于10就会算错。

	// 计算算术表达式
	// 需要两个栈,一个运算符栈,一个操作数栈
	stack<char> opstack;
	stack<int> valstack;
	string s3 = "1*(4+3)+1";

	int val2 = CalExpression(valstack, opstack, s3);
	cout << "计算中缀算术表达式" << s3 << "得:" << endl << val2 << endl;

	return 0;
}

在这里插入图片描述

四、串(暂略)

五、树与二叉树

1.二叉树

请添加图片描述

1.1二叉树的顺序存储

请添加图片描述

#include <stdio.h> 
#include <cmath>

#define TreeSize 99 
#define MaxSize 100
/* 
以完全二叉树形式存储结点
i的左孩子:2i
i的右孩子:2i+1
i的父节点:i/2 向下取整
i所在的层次(深度):log2 (n) + 1
*/
struct TreeNode {
    
    
	int value; 		// 节点中的数据元素 
	bool isEmpty; 	// 结点是否为空 
};
// 初始化
void InitTreeNode(TreeNode(&t)[MaxSize]) {
    
    
	for (int i = 0; i < MaxSize; i++) {
    
    
		t[i].isEmpty = true; // 都为空
	}
}
// 位序i结点的左孩子 
TreeNode GetLeftC(TreeNode(&t)[MaxSize], int i) {
    
    
	TreeNode tn;
	tn.isEmpty = true;
	int j = i * 2;
	if (j <= TreeSize && !t[j].isEmpty && !t[i].isEmpty) {
    
    // 没越界 且有左孩子 且自己不是空 
		tn = t[j];
	}
	return tn;
}
// 位序i结点的右孩子 
TreeNode GetRightC(TreeNode(&t)[MaxSize], int i) {
    
    
	TreeNode tn;
	tn.isEmpty = true;
	int j = i * 2 + 1;
	if (j <= TreeSize && !t[j].isEmpty && !t[i].isEmpty) {
    
    // 没越界 且有右孩子 且自己不是空 
		tn = t[j];
	}
	return tn;
}
// 位序i结点的父结点 
TreeNode GetParentC(TreeNode(&t)[MaxSize], int i) {
    
    
	TreeNode tn;
	tn.isEmpty = true;
	int j = i / 2;
	if (j >= 1 && !t[j].isEmpty) {
    
    // 没越界 且有父亲 
		tn = t[j];
	}
	return tn;
}
// 位序i结点层次(深度) 
int GetDepth(TreeNode(&t)[MaxSize], int i) {
    
    
	if (i >= 1 && i <= TreeSize) {
    
    // 是否越界 
		return log(i) / log(2) + 1;
	}
	return 0;
}
// 在位序i的位置插入一个结点
bool InsertTreeNode(TreeNode(&t)[MaxSize], int i, int e) {
    
    
	if (i < 1 || i > TreeSize) {
    
    // 是否越界 
		return false;
	}
	TreeNode tn;
	tn.isEmpty = false;
	tn.value = e;
	t[i] = tn;
	return true;
}
// 删除在位序i的位置上的结点 - 要注意把他的左右孩子也删除
bool DeleteTreeNode(TreeNode(&t)[MaxSize], int i, int& e) {
    
    
	if (i < 1 || i > TreeSize) {
    
    // 是否越界 
		return false;
	}
	// 递归把它的左右孩子也删除
	DeleteTreeNode(t, i * 2, e);
	DeleteTreeNode(t, i * 2 + 1, e);

	e = t[i].value;
	t[i].isEmpty = true;
	return true;
}
// 顺序遍历 
void PrintTreeNodeOrder(TreeNode(&t)[MaxSize]) {
    
    
	for (int i = 1; i < MaxSize; i++) {
    
    
		if (!t[i].isEmpty) {
    
    
			printf("%d  ", t[i]);
		}
	}
}
// 遍历各个结点详细信息
void PrintTreeNodeAll(TreeNode(&t)[MaxSize], int maxn) {
    
    
	TreeNode tn1, tn2, tn3;
	int depth;
	for (int i = 1; i < maxn; i++) {
    
    
		if (t[i].isEmpty) {
    
    
			printf("%d结点为空\n", i);
			continue;
		}
		tn1 = GetLeftC(t, i);	// 左结点
		tn2 = GetRightC(t, i);  // 有结点
		tn3 = GetParentC(t, i); // 父亲结点
		if (!tn3.isEmpty) {
    
    
			printf("%d结点的父结点是:%d\t", i, tn3.value);
		}
		else {
    
    
			printf("%d结点没有父结点 \t", i);
		}
		if (!tn1.isEmpty) {
    
    
			printf("%d结点的左结点是:%d\t", i, tn1.value);
		}
		else {
    
    
			printf("%d结点没有左结点 \t", i);
		}
		if (!tn2.isEmpty) {
    
    
			printf("%d结点的右结点是:%d\t", i, tn2.value);
		}
		else {
    
    
			printf("%d结点没有右结点 \t", i);
		}
		depth = GetDepth(t, i);
		printf("%d结点的深度是%d\n", i, depth);
	}
}
int main() {
    
    
	TreeNode t[MaxSize];
	InitTreeNode(t);
	// 插入节点
	int maxn = 16;
	for (int i = 1; i < maxn; i++) {
    
    
		InsertTreeNode(t, i, i);
	}
	printf("依次输出各个结点:\n");
	PrintTreeNodeOrder(t);
	printf("\n");
	printf("依次输出每个结点的详细信息:左右孩子、父亲、深度:\n");
	PrintTreeNodeAll(t, maxn);
	printf("\n");
	// 删除节点
	int e;
	DeleteTreeNode(t, 2, e);
	printf("删除结点2后,依次输出各个结点:\n");
	PrintTreeNodeOrder(t);
	printf("\n");
	printf("删除结点2后,各个结点详细信息:\n");
	PrintTreeNodeAll(t, maxn);
}

构造的二叉树图:
在这里插入图片描述
删除结点2的二叉树图:
在这里插入图片描述
请添加图片描述

1.2二叉树的链式存储

#include <stdio.h> 
#include <stdlib.h>
#include <cmath>

typedef struct BiTNode {
    
    
	int data; 		// 数据域 
	struct BiTNode* lchild, * rchild;// 左右孩子指针 
	struct BiTNode* parent;// 父亲结点 
}BiTNode, * BiTree;
// 插入根结点 
bool InsertRootNode(BiTree& btree, int e) {
    
    
	btree = (BiTree)malloc(sizeof(BiTNode));
	btree->data = e;
	btree->lchild = NULL;
	btree->rchild = NULL;
	btree->parent = NULL;
	return true;
}
// 1.当前p结点的左孩子 
BiTNode* GetLeftC(BiTree& p) {
    
    
	if (p == NULL || p->lchild == NULL) {
    
    
		return NULL;
	}
	return p->lchild;
}
// 2.当前p结点的右孩子  
BiTNode* GetRightC(BiTree& p) {
    
    
	if (p == NULL || p->rchild == NULL) {
    
    
		return NULL;
	}
	return p->rchild;
}
// 3.当前p结点的父亲  
BiTNode* GetParent(BiTree& p) {
    
    
	if (p == NULL || p->parent == NULL) {
    
    
		return NULL;
	}
	return p->parent;
}
// 当前结点c插入一个左结点
bool InsertTreeLeftNode(BiTree& c, int e) {
    
    
	BiTNode* p = (BiTNode*)malloc(sizeof(BiTNode));
	p->data = e;
	p->lchild = NULL;
	p->rchild = NULL;
	p->parent = c;

	c->lchild = p;
	return true;
}
// 当前c结点插入一个右结点
bool InsertTreeRightNode(BiTree& c, int e) {
    
    
	BiTNode* p = (BiTNode*)malloc(sizeof(BiTNode));
	p->data = e;
	p->lchild = NULL;
	p->rchild = NULL;
	p->parent = c;

	c->rchild = p;
	return true;
}
// 删除c节点 : 要删除c结点的左右结点,并且释放空间
bool DeleteTreeNode(BiTree& c) {
    
    
	BiTNode* curp = c;
	BiTNode* p;
	while (curp != NULL) {
    
     // 扫描左结点
		p = curp->lchild;
		if (p != NULL) {
    
    
			curp = p->lchild;// 当前结点被删除之前获取当前结点的左结点,以便下次循环
		}
		else {
    
    
			curp = NULL;
		}
		free(p);
	}
	curp = c;// 当前节点开始扫描右节点 
	while (curp != NULL) {
    
    
		p = curp->rchild;
		if (p != NULL) {
    
    
			curp = p->rchild;
		}
		else {
    
    
			curp = NULL;
		}
		free(p);
	}
	// 注意此结点的父节点的左或者右结点置空。 
	if (c->parent->lchild == c) {
    
    
		c->parent->lchild = NULL;
	}
	else {
    
    
		c->parent->rchild = NULL;
	}
	free(c);// 释放自己 
	return true;
}
// 判断二叉树是否为空
bool Empty(BiTree& root) {
    
    
	if (root == NULL) {
    
    
		return true;
	}
	return false;
}
// 辅助方法:中序遍历 
void InOrder(BiTree p) {
    
    // 不能引用类型 
	if (p != NULL) {
    
    
		InOrder(p->lchild);
		printf("%d  ", p->data);
		InOrder(p->rchild);
	}
}
int main() {
    
    
	// 手动构造二叉树 
	BiTree root = NULL;
	InsertRootNode(root, 1);
	BiTree cur = root;// cur = 1
	InsertTreeLeftNode(cur, 2);
	InsertTreeRightNode(cur, 3);

	cur = root->lchild;// cur = 2
	InsertTreeLeftNode(cur, 4);
	InsertTreeRightNode(cur, 5);

	cur = root->rchild;// cur = 3
	InsertTreeLeftNode(cur, 6);
	InsertTreeRightNode(cur, 7);

	cur = root->lchild->lchild;// cur = 4
	InsertTreeLeftNode(cur, 8);
	InsertTreeRightNode(cur, 9);

	cur = root->lchild->rchild;// cur = 5
	InsertTreeLeftNode(cur, 10);
	InsertTreeRightNode(cur, 11);

	cur = root->rchild->lchild;// cur = 6
	InsertTreeLeftNode(cur, 12);
	InsertTreeRightNode(cur, 13);

	cur = root->rchild->rchild;// cur = 7
	InsertTreeLeftNode(cur, 14);
	InsertTreeRightNode(cur, 15);

	// 测试获取左右结点 父结点方法
	cur = GetLeftC(root->lchild->lchild);
	if (cur != NULL) {
    
    
		printf("结点%d的左结点是%d\n", root->lchild->lchild->data, cur->data);
	}
	cur = GetRightC(root->rchild->rchild);
	if (cur != NULL) {
    
    
		printf("结点%d的右结点是%d\n", root->rchild->rchild->data, cur->data);
	}
	cur = GetParent(root->lchild->rchild);
	if (cur != NULL) {
    
    
		printf("结点%d的父亲结点是%d\n", root->lchild->rchild->data, cur->data);
	}
	printf("中序遍历:\n", root->lchild->data);
	InOrder(root);
	// 删除节点
	printf("\n删除结点%d后中序遍历:\n", root->lchild->data);
	DeleteTreeNode(root->lchild);
	InOrder(root);
	printf("\n");
}

构造的二叉树图与删除结点2的二叉树图与前一致
请添加图片描述

1.3二叉树前后中递归遍历、非递归遍历、层次遍历(链式存储)

非递归遍历是难点,其中后序非递归遍历更是较难,建议跟着代码调试一步一步看更能理解

#include <stdio.h> 
#include <stdlib.h>
#include <cmath> // 为了实现log运算,导入cmath库
#include <queue> // 借助队列,实现层次遍历
#include <stack> // 借助栈,实现递推遍历
using namespace std;
typedef struct TNodeData {
    
    
	int data; // 值 
	int index;// 位序,可以得出深度 
}TNodeData;

typedef struct BiTNode {
    
    
	TNodeData* tdata; 		// 数据域 
	struct BiTNode* lchild, * rchild;// 左右孩子指针 
	struct BiTNode* parent;// 父亲结点 
}BiTNode, * BiTree;

// 插入根结点 
bool InsertRootNode(BiTree& btree, int e) {
    
    
	btree = (BiTree)malloc(sizeof(BiTNode));
	TNodeData* td = (TNodeData*)malloc(sizeof(TNodeData));
	td->data = e;
	td->index = 1;// 根结点的位序为1 

	btree->tdata = td;
	btree->lchild = NULL;
	btree->rchild = NULL;
	btree->parent = NULL;
	return true;
}

// 1.当前p结点的左孩子 
BiTNode* GetLeftC(BiTree& p) {
    
    
	if (p == NULL || p->lchild == NULL) {
    
    
		return NULL;
	}
	return p->lchild;
}
// 2.当前结点的右孩子  
BiTNode* GetRightC(BiTree& p) {
    
    
	if (p == NULL || p->rchild == NULL) {
    
    
		return NULL;
	}
	return p->rchild;
}
// 3.当前p结点的父亲  
BiTNode* GetParent(BiTree& p) {
    
    
	if (p == NULL || p->parent == NULL) {
    
    
		return NULL;
	}
	return p->parent;
}
// 4.获取当前p结点的深度
int GetDepth(BiTree& p) {
    
    
	if (p == NULL) {
    
    
		return 0;
	}
	return (log(p->tdata->index) / log(2) + 1);
}
// 当前c结点插入一个左结点
bool InsertTreeLeftNode(BiTree& c, int e) {
    
    
	BiTNode* p = (BiTNode*)malloc(sizeof(BiTNode));
	TNodeData* td = (TNodeData*)malloc(sizeof(TNodeData));
	td->data = e;
	td->index = c->tdata->index * 2;// 根据c结点得出位序

	p->tdata = td;
	p->lchild = NULL;
	p->rchild = NULL;
	p->parent = c;

	c->lchild = p;
	return true;
}
// 当前c结点插入一个右结点
bool InsertTreeRightNode(BiTree& c, int e) {
    
    
	BiTNode* p = (BiTNode*)malloc(sizeof(BiTNode));
	TNodeData* td = (TNodeData*)malloc(sizeof(TNodeData));
	td->data = e;
	td->index = c->tdata->index * 2 + 1;// 根据c结点得出位序

	p->tdata = td;
	p->lchild = NULL;
	p->rchild = NULL;
	p->parent = c;

	c->rchild = p;
	return true;
}
// 删除c节点 : 要删除c结点的左右结点,并且释放空间
bool DeleteTreeNode(BiTree& c) {
    
    
	BiTNode* curp = c;
	BiTNode* p;
	while (curp != NULL) {
    
    // 扫描左结点
		p = curp->lchild;
		if (p != NULL) {
    
    
			curp = p->lchild;// 当前结点被删除之前获取当前结点的左结点,以便下次循环
		}
		else {
    
    
			curp = NULL;
		}
		free(p);
	}
	curp = c;// 当前节点开始扫描右节点 
	while (curp != NULL) {
    
    
		p = curp->rchild;
		if (p != NULL) {
    
    
			curp = p->rchild;
		}
		else {
    
    
			curp = NULL;
		}
		free(p);
	}
	// 注意此结点的父节点的左右结点置空。 
	if (c->parent->lchild == c) {
    
    
		c->parent->lchild = NULL;
	}
	else {
    
    
		c->parent->rchild = NULL;
	}
	free(c);// 释放自己 
	return true;
}
// 判断二叉树是否为空
bool Empty(BiTree& root) {
    
    
	if (root == NULL) {
    
    
		return true;
	}
	return false;
}
// 本小节重点------------------------------------------------
// 前序遍历 
void PreOrder(BiTree p) {
    
    // 不能引用类型 
	if (p != NULL) {
    
    
		printf("%d  ", p->tdata->data);
		PreOrder(p->lchild);
		PreOrder(p->rchild);
	}
}
// 中序遍历 
void InOrder(BiTree p) {
    
    // 不能引用类型 
	if (p != NULL) {
    
    
		InOrder(p->lchild);
		printf("%d  ", p->tdata->data);
		InOrder(p->rchild);
	}
}
// 后序遍历 
void PostOrder(BiTree p) {
    
    // 不能引用类型 
	if (p != NULL) {
    
    
		PostOrder(p->lchild);
		PostOrder(p->rchild);
		printf("%d  ", p->tdata->data);
	}
}
// 层次遍历 
void CenCi(BiTree& root) {
    
     
	queue<BiTNode*> que;
	que.push(root);
	BiTNode* p;
	while (!que.empty()) {
    
    
		p = que.front();
		que.pop();
		printf("%d  ", p->tdata->data);
		if (p->lchild != NULL) {
    
    
			que.push(p->lchild);
		}
		if (p->rchild != NULL) {
    
    
			que.push(p->rchild);
		}
	}
}
// 难点------------------------------------------------
// 递推遍历
void PreOrderDiTui(BiTree& root) {
    
    // 前序
	stack<BiTNode*> bistack;
	BiTNode* p = root;
	while (p != NULL || !bistack.empty()) {
    
    
		if (p != NULL) {
    
    
			printf("%d  ", p->tdata->data);
			bistack.push(p);
			p = p->lchild;
		}
		else {
    
    
			p = bistack.top();
			bistack.pop();
			p = p->rchild;
		}
	}
	printf("\n");
}
void InOrderDiTui(BiTree& root) {
    
    // 中序 
	stack<BiTNode*> bistack;
	BiTNode* p = root;
	while (p != NULL || !bistack.empty()) {
    
    
		if (p != NULL) {
    
    
			bistack.push(p);
			p = p->lchild;
		}
		else {
    
    
			p = bistack.top();
			bistack.pop();
			printf("%d  ", p->tdata->data);
			p = p->rchild;
		}
	}
	printf("\n");
}
void PostOrderDiTui(BiTree& root) {
    
    // 后序 
	stack<BiTNode*> bistack;
	BiTNode* p = root;
	BiTNode* lastp = NULL;// 上一个访问的结点 ,关键点!,用来判断是否右子树返回 
	while (p != NULL || !bistack.empty()) {
    
    
		// 左子树依次入栈 
		if (p != NULL) {
    
    
			bistack.push(p);
			p = p->lchild;
		}
		else {
    
    
			// 没路走,父结点出栈需要满足以下两个条件之一
			// 1.右子树为空出栈
			// 2.右子树访问完返回也可以出栈
			p = bistack.top();
			if (p->rchild == NULL || p->rchild == lastp) {
    
    
				lastp = p;
				bistack.pop();
				printf("%d  ", p->tdata->data);
				p = NULL; // 记住置为空,写错过!!! 
			}
			else {
    
    
				// 1.1右子树不空,往右子树走 
				p = p->rchild;
			}
		}
	}
	printf("\n");
}
int main() {
    
    
	// 手动构造二叉树 
	BiTree root = NULL;
	InsertRootNode(root, 1);
	BiTree cur = root;// cur = 1
	InsertTreeLeftNode(cur, 2);
	InsertTreeRightNode(cur, 3);

	cur = root->lchild;// cur = 2
	InsertTreeLeftNode(cur, 4);
	InsertTreeRightNode(cur, 5);

	cur = root->rchild;// cur = 3
	InsertTreeLeftNode(cur, 6);
	InsertTreeRightNode(cur, 7);

	cur = root->lchild->lchild;// cur = 4
	InsertTreeLeftNode(cur, 8);
	InsertTreeRightNode(cur, 9);

	cur = root->lchild->rchild;// cur = 5
	InsertTreeLeftNode(cur, 10);
	InsertTreeRightNode(cur, 11);

	cur = root->rchild->lchild;// cur = 6
	InsertTreeLeftNode(cur, 12);
	InsertTreeRightNode(cur, 13);

	cur = root->rchild->rchild;// cur = 7
	InsertTreeLeftNode(cur, 14);
	InsertTreeRightNode(cur, 15);

	printf("测试结点的深度:\n");
	int hight = GetDepth(root->lchild->rchild);
	printf("结点%d的深度是%d\n", root->lchild->rchild->tdata->data, hight);

	hight = GetDepth(root->rchild->rchild);
	printf("结点%d的深度是%d\n", root->rchild->rchild->tdata->data, hight);

	hight = GetDepth(root->rchild->rchild->rchild);
	printf("结点%d的深度是%d\n", root->rchild->rchild->rchild->tdata->data, hight);

	printf("前序遍历:\n");
	PreOrder(root);
	printf("\n中序遍历:\n");
	InOrder(root);
	printf("\n后序遍历:\n");
	PostOrder(root);
	printf("\n层次遍历:\n");
	CenCi(root);

	// 删除节点
	printf("\n删除结点%d后递推遍历二叉树:\n", root->lchild->tdata->data);
	DeleteTreeNode(root->lchild);
	printf("递推前序遍历:\n");
	PreOrderDiTui(root);
	printf("递推中序遍历:\n");
	InOrderDiTui(root);
	printf("递推后序遍历:\n");
	PostOrderDiTui(root);
	printf("层次遍历:\n");
	CenCi(root);
}

构造的二叉树图与删除结点2的二叉树图与前一致
请添加图片描述

1.4由先序遍历序列构造二叉树(链式存储)

请添加图片描述

#include <iostream>
#include <algorithm>
using namespace std;
// 实现二叉树的创建和销毁二叉树、三种遍历、求深度、叶子结点数、结点数等相关算法。

typedef struct BiTNode {
    
    
	int data;
	struct BiTNode* lchild, * rchild;
}*BiTree;

int e;
// 根据先序输入创建二叉树
void CreateBiTree(BiTree& T) {
    
    
	cin >> e;
	if (e == 0) {
    
    
		T = NULL;
		return;
	}
	T = new BiTNode(); // 生成结点
	T->data = e;
	CreateBiTree(T->lchild);
	CreateBiTree(T->rchild);
}
// 额外补充----------------------------------------------
// 后序遍历销毁二叉树
void DeleteBiTree(BiTree& T) {
    
    
	if (T == NULL)
		return;
	DeleteBiTree(T->lchild);
	DeleteBiTree(T->rchild);
	cout << "元素为" << T->data << "的结点删除完成" << endl;
	delete T;
}
// 前序遍历
void PreOrder(BiTree& T) {
    
    
	if (T == NULL)
		return;
	cout << T->data << " ";
	PreOrder(T->lchild);
	PreOrder(T->rchild);
}
// 中序遍历
void InOrder(BiTree& T) {
    
    
	if (T == NULL)
		return;
	InOrder(T->lchild);
	cout << T->data << " ";
	InOrder(T->rchild);
}
// 后序遍历
void PostOrder(BiTree& T) {
    
    
	if (T == NULL)
		return;
	PostOrder(T->lchild);
	PostOrder(T->rchild);
	cout << T->data << " ";
}
// 深度
int GetHeight(BiTree& T) {
    
    
	if (T == NULL)
		return 0;
	return 1 + max(GetHeight(T->lchild), GetHeight(T->rchild)); // 自己的高度加上左右孩子谁更高就是这颗子树的高度,再往上递归就是整棵树的高度
}
// 求叶子结点数
int GetBiTreeLeafSize(BiTree& T) {
    
    
	if (T == NULL)
		return 0;
	// 只有左右孩子为空的结点才是叶子结点
	if (T->lchild == NULL && T->rchild == NULL) {
    
    
		return 1;
	}
	return 0 + GetBiTreeLeafSize(T->lchild) + GetBiTreeLeafSize(T->rchild);
}
// 求结点数
int GetBiTreeSize(BiTree& T) {
    
    
	if (T == NULL)
		return 0;
	return 1 + GetBiTreeSize(T->lchild) + GetBiTreeSize(T->rchild);
}
int main() {
    
    
	BiTNode* biTree;
	CreateBiTree(biTree); // 创建二叉树
	cout << "二叉树创建完成" << endl;

	cout << "前序遍历:" << endl;
	PreOrder(biTree);// 前序遍历
	cout << endl;

	cout << "中序遍历:" << endl;
	InOrder(biTree);// 中序遍历
	cout << endl;

	cout << "后序遍历:" << endl;
	PostOrder(biTree);// 后序遍历
	cout << endl;

	// 获取深度
	cout << "这颗二叉树的深度是:" << GetHeight(biTree) << endl;
	// 获取叶子结点数
	cout << "这颗二叉树的叶子结点数是:" << GetBiTreeLeafSize(biTree) << endl;
	// 获取树结点数
	cout << "这颗二叉树的结点数是:" << GetBiTreeSize(biTree) << endl;

	DeleteBiTree(biTree);// 删除二叉树
	cout << "二叉树销毁完成" << endl;
	return 0;
}
/*
1 2 3 0 0 4 5 0 6 0 0 7 0 0 8 0 0
构造的二叉树如:
		 1
	 2		        8
  3     4
	  5	  7
	   6

*/

构造的二叉树图:
在这里插入图片描述
请添加图片描述

1.5树的存储结构

1.5.1双亲表示法

#include <stdio.h> 
#include <stdlib.h>
#define MAX_TREE_SIZE 100

using namespace std;
typedef struct {
    
    
	char data;// 值 
	int index;	// 自己在数组中的下标 
	int parent;// 双亲位置 ,在数组中的下标 
}PTNode;

typedef struct {
    
    							// 数的类型定义
	PTNode nodes[MAX_TREE_SIZE]; 		// 双亲表示 
	int n;								// 结点数					
}PTree;

// 0.初始化
void InitTree(PTree& pt) {
    
    
	pt.n = 0;
}
// 1.插入根结点 
bool InsertRootNode(PTree& ptree, char e) {
    
    
	PTNode ptn;
	ptn.data = e;			// 元素值 
	ptn.parent = -1;			// 没有父亲 
	ptn.index = 0;			// 自己在数组的位置为0 
	ptree.nodes[ptree.n] = ptn; 	// 根放入数组中第一个位置 下标为0 
	ptree.n++;				// 结点数加1 
	return true;
}
// 2.当前结点的父亲 
PTNode *GetParent(PTree &ptree, PTNode &ptn){
    
    
	if(ptn.parent == -1){
    
    
		return NULL;
	}
	return &ptree.nodes[ptn.parent];
} 
// 3.p结点插入一个叶子结点ptn
bool InsertPTNode(PTree& ptree, PTNode& p, char e) {
    
    
	if (ptree.n >= MAX_TREE_SIZE) {
    
    
		return false;
	}
	PTNode ptn;
	ptn.data = e;
	ptn.index = ptree.n;			//当前n结点数就是自己的下标 
	ptn.parent = p.index;	    // 父亲下标
	ptree.nodes[ptree.n] = ptn;// 放到末尾
	ptree.n++;				// 结点数加 
	return true;
}
// 4.根据下标删除结点:较难。 
bool DeleteTreeNode(PTree& ptree, int index) {
    
    
	// 方法一:将值置为'0'代表删除,并将parent置为--1。 并将这个结点的子节点都删除
	ptree.nodes[index].data = '0';
	ptree.nodes[index].parent = -1;
	for (int i = 0; i < ptree.n; i++) {
    
    
		if (ptree.nodes[i].parent == index) {
    
    
			ptree.nodes[i].data = '0';
			ptree.nodes[i].parent = -1;
		}
	}
 	return true;
}
// 5.判断为空
bool Empty(PTree& ptree) {
    
    
	if (ptree.n == 0) {
    
    
		return true;
	}
	return false;
}
// 辅助方法 
// 打印ptn结点的孩子们=实现查找一个结点的孩子 
void PrintPTNodeChildInfo(PTree& ptree, PTNode& ptn) {
    
    
	PTNode ptnchild;
	for (int i = 0; i < ptree.n; i++) {
    
    
		if (i == 0) {
    
    
			printf("%c的孩子有:", ptn.data);
		}
		ptnchild = ptree.nodes[i];
		if (ptnchild.parent == ptn.index) {
    
    
			printf("%c(%d) ", ptnchild.data, ptnchild.index);
		}
	}
}
// 依次打印结点
void PrintPTNodeInfo(PTree& ptree) {
    
    
	PTNode ptn;
	for (int i = 0; i < ptree.n; i++) {
    
    
		// 删除了的结点跳过
		if (ptree.nodes[i].parent == -1 && i != 0) {
    
    
			continue;
		}
		ptn = ptree.nodes[i];
		printf("第%d个下标的值是%c,", i, ptn.data);
		// 打印父亲 
		if (ptn.parent != -1) {
    
    
			printf("它的父结点的是%c(%d),",ptree.nodes[ptn.parent].data, ptn.parent);
		}
		// 打印孩子 
		//printf("\n");
		PrintPTNodeChildInfo(ptree, ptn);
		printf("\n");
	}
}
void PrintEmpty(PTree& ptree) {
    
    
	if (Empty(ptree)) {
    
    
		printf("树为空");
	}
	else {
    
    
		int ct = 1;
		for (int i = 1; i < ptree.n; i++) {
    
     
			if (ptree.nodes[i].parent != -1) {
    
    
				ct++;
			}
		}
		printf("树不为空,有%d个结点", ct);
	}
	printf("\n");
}
int main() {
    
    
	PTree ptree;
	InitTree(ptree);// 初始化
	PrintEmpty(ptree);// 是否为空

	// 手动构造树
	InsertRootNode(ptree, 'R');
	// 插入结点 
	InsertPTNode(ptree, ptree.nodes[0], 'A');
	InsertPTNode(ptree, ptree.nodes[0], 'B');
	InsertPTNode(ptree, ptree.nodes[0], 'C');

	InsertPTNode(ptree, ptree.nodes[1], 'D');
	InsertPTNode(ptree, ptree.nodes[1], 'E');

	InsertPTNode(ptree, ptree.nodes[3], 'F');

	InsertPTNode(ptree, ptree.nodes[6], 'G');
	InsertPTNode(ptree, ptree.nodes[6], 'H');
	InsertPTNode(ptree, ptree.nodes[6], 'K');

	PrintEmpty(ptree);// 是否为空

	printf("遍历各结点信息:\n");
	PrintPTNodeInfo(ptree);

	printf("\n删除下标为1的结点后,遍历各结点信息:\n");
	DeleteTreeNode(ptree, 1);// 删除节点
	PrintEmpty(ptree);// 是否为空

	PrintPTNodeInfo(ptree);
}

构造的树
在这里插入图片描述
删除A结点后的树
在这里插入图片描述
运行结果
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_34060370/article/details/123579883#comments_28250417