数据结构
@Author chummyj
一、线性表
1 顺序表
#include <iostream>
#include <iterator>
#include <stdexcept>
using namespace std;
#define MAXSIZE 100
typedef struct{
int *data;
int length;
}SqList;
//Initalize
bool InitList(SqList &L){
L.data = new int[MAXSIZE]; //Allocate pace
if (!L.data) {
cout<<“Failed to allocate space!\n”;
return false;
}
L.length = 0;
return true;
}
// Insert the element at the i position
bool ListInsert(SqList &L, int i, int e){
if (i < 1 || i > L.length+1){
cout<<“Position ERROR”<<endl;
return false;
}
if (L.length == MAXSIZE) {
cout<<“The storag space is full!”<<endl;
return false;
}
for (int j = L.length-1; j > i-1; j--) {
L.data[j+1] = L.data[j];
}
L.data[i-1] = e;
L.length++;
return true;
}
// Get data at the ith postition
bool GetData(SqList L, int i, int e){
//Determine if th location is reasonable
if (i < 1 || i > L.length) {
cout<<“Postition ERROR!!”<<endl;
return false;
}
e = L.data[i-1];
cout<<“data at the”<<i<<“postition:”<<e<<endl;
return true;
}
// Locatedata of Postition
int LocateData(SqList L, int e){
for (int i = 0; i < L.length; i++) {
if (L.data[i] == e) {
return i+1;
}
}
return 0;
}
// Delete data at the ith postition
bool DeleteData(SqList &L, int i){
if (i < 1 || i > L.length) {
cout<<“Postition ERROR!!”<<endl;
return false;
}
for (int j = i-1; j < L.length-1; j++) {
L.data[j+1] = L.data[j];
}
L.length--;
cout<<“return data by delete:”<<L.data[i-1]<<endl;
return true;
}
// InsertMultiple
bool InsertMultiple(SqList &L, int n){
cout<<“Please print number of insert:”<<endl;
cin>>n;
if(n < 1) return false;
for (int j = 0; j < n; j++) {
int i = 0;
int e;
cout<<“Please print postition of insert:”<<endl;
cin>>i;
cout<<“Please print data of insert:”<<endl;
cin>>e;
ListInsert(L, i, e);
}
return true;
}
// Print
void PrintList(SqList L){
for (int i = 0; i < L.length; i++) {
cout<<“data in the SqList:”<<L.data[i]<<endl;
}
}
int main() {
SqList L;
int n,i = 0;
InitList(L);
InsertMultiple(L, n);
PrintList(L);
return 0;
}
2 链表
#include <cstddef>
#include <iostream>
using namespace std;
//define struct
typedef struct LNode{
int data;
struct LNode *next;
}LNode, *LinkList;
//Initialize
bool InitLNode(LinkList &L){
L = new LNode;
L->next=NULL;
return true;
}
//Create LNode of HeadInsert
LinkList CreateHeadInsert(LinkList &L, int n){
L = new LNode;
L->next = NULL;
for (int i = 0; i < n; i++) {
LinkList p = new LNode;
cin>>p->data;
p->next = L->next;
L->next = p;
}
return L;
}
//Create LNode of TailInsert
LinkList CreateLNodeTail(LinkList &L, int n){
LNode *r = L;
for (int i = 0; i < n; i++) {
LinkList p = new LNode;
cin>>p->data;
r->next = p;
p->next = NULL;
r = p;
}
return L;
}
//Insert data(e) at the ith postition
bool InsertLinkList(LinkList &L, int i, int e){
LNode *p = L;
int j = 0;
if (L->next == NULL){
//cout<<“LinkList is NULL!”<<endl;
return false;
}
//Locate postition
while (p && j < i-1) {
p = p->next;
j++;
}
if (!p || j > i-1){ // i < 1 or i > n+1
//cout<<“Postition ERROR!!”<<endl;
return false;
}
LinkList s = new LNode;
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
//Locate data at the ith postition
LNode *LocateData(LinkList L, int e){
LNode *p = L->next;
while (p && p->data!= e) {
p = p->next;
}
return p;
}
//Delete data at the ith postition
bool DeleteLinkList(LinkList &L, int i){
LNode *p = L;
int j = 0;
if (L->next == NULL){
//cout<<“LinkList is NULL!”<<endl;
return false;
}
//Locate postition
while (p->next && j < i-1) {
p = p->next;
j++;
}
if (!p->next || j > i-1){ // i < 1 or i > n+1
//cout<<“Postition ERROR!!”<<endl;
return false;
}
LNode *q = p->next;
p->next = q->next;
delete q;
return true;
}
//PrintNodedata
void PrintNodedata(LinkList L){
LNode *p = L->next;
if (p == NULL) {
cout<<“List is null!”<<endl;
}
while (p) {
cout<<“LinkList of value:”<<p->data<<endl;
p = p->next;
}
}
int main() {
LinkList L = new LNode;
int n,i,e,k;
InitLNode(L);
cout<<“Please print the number of insert:”<<endl;
cin>>n;
CreateHeadInsert(L, n);
PrintNodedata(L);
cout<<“Please print insert data of postition:”<<endl;
cin>>i;
cout<<“Please, print insert data of value:”<<endl;
cin>>e;
InsertLinkList(L, i, e);
PrintNodedata(L);
cout<<“Please, delete data of postition:”<<endl;
cin>>k;
DeleteLinkList(L, k);
PrintNodedata(L);
return 0;
}
3 线性表的应用
多项式加法
#include <cstdlib>
#include <iostream>
#include <pthread.h>
using namespace std;
// 数据结构的定义
typedef struct PNode{
float coef; //系数
int expn; //指数
struct PNode *next;
}PNode, *Polynomial;
// 多项式的创建
/*
1. 创建一个只有头节点的空链表
2.根据多项式项数的个数n,循环n次执行:
生成一个新节点*s;
输入多项式当前项的系数和指数赋给新节点*s
设置一个前驱节点pre,用于指向代找到的第一个大于输入项指数的结点的前驱,pre初值指向头结点
指针q初始化,指向首元结点
循链向下逐个比较链表中当前结点与输入项指数,找到第一个大于输入项指数的结点*q
将输入项结点 *s插入到结点*q之前
*/
Polynomial CreatePolyn(Polynomial &P, int n)
{
// 输入n项的系数和指数,建立表示多项式的有序链表
P = new PNode;
PNode *p=P;
p->next = NULL;
for(int i=1; i<=n;i++){
Polynomial s = new PNode;
cout<<“请输入第:”<<i<<“个多项式的系数和指数\n”;
cin>>s->coef>>s->expn;
PNode *pre = p;
PNode *q = p->next;
//循链向下逐个比较链表中当前结点与输入项指数,找到第一个大于输入项指数的结点
while(q&&q->expn<s->expn){
pre = q;
q = q->next;
}
//将输入项结点*s插入到结点*q之前
s->next = q;
pre->next = s;
}
return P;
}
Polynomial AddPolyn(Polynomial &Pa, Polynomial &Pb)
{
PNode *p1 = Pa->next;
PNode *p2 = Pb->next;
PNode *p3 = Pa; // Point to the current node of the polynmial
while (p1&&p2) {
if(p1->expn == p2->expn){
int sum = p1->coef + p2->coef;
if (sum != 0) {
p1->coef = sum;
p3->next = p1; // Insert the new(add) of node
p3 = p1;
p1 = p1->next; //Point to the next node of p1
// delete the node point to by p2
PNode *r = p2;
p2 = p2->next;
// delete r;
free(r);
//r = p2;
}
else { //else: delete the node point to by p1 or p2
PNode *r = p1;
p1 = p1->next;
free(p1);
//r=p1;
/*
r = p2;
p2 = p2->next;
free(p2);
r = p2;*/
}
}
else if (p1->coef < p2->coef) {
p3->next = p1;
p3 = p1;
p1 = p1->next;
}
else {
p3->next = p2;
p3 = p2;
p2 = p2->next;
}
}
p3->next = p1?p1:p2;
// delete Pb;
return Pa;
}
void Print(Polynomial P)
{
PNode *p = P->next;
while (p) {
cout<<p->coef<<“x^”<<p->expn<<endl;
p = p->next;
}
}
int main() {
int n1,n2;
Polynomial Pa = new PNode;
Polynomial Pb = new PNode;
cout << “输入第一个多项式的项数:\n”;
cin>>n1;
Pa = CreatePolyn(Pa,n1);
cout << “输入第二个多项式的项数:\n”;
cin>>n2;
Pb = CreatePolyn(Pb, n2);
Polynomial P = new PNode;
P = AddPolyn(Pa, Pb);
Print(P);
return 0;
}
二、栈与队列
1 栈
顺序栈
#include<iostream>
using namespace std;
#define MAXSIZE 100
typedef struct{
ElemType data[MAXSIZE]
int top;
}SqStack;
// InitStack
void InitStack(SqStck &S){
S.top = -1;
}
// StackEmpty
bool StackEmpty(SqStack S){
if(S.top == -1)
return true;
else
return false;
}
// Push
bool Push(SqStack &S, ElemType e){
if(S.top == MAXSIZE-1)
return false;
S.data[++S.top] = e; // 先加1,再进栈
return true;
}
// Pop
bool Pop(SqStack &S, ElemType &e){
if(S.top == -1)
return false;
e = S.data[S.top--]; // 先减1,再出栈
return true;
}
// GetTop
bool GetTop(SqStack S, ElemType &e){
if(S.top == -1)
return false;
e = S.data[S.top]; // 获取栈顶元素
}
/*
双端栈 top0 = -1时0号栈为空
top1 = MAXSIZE 时1号栈为空
top1 - top0 == 1 时沾满
同上可以写出双端栈的
*/
链栈
typedef struct StackNode{
ElemType data;
struct StackNode *next;
}StackNode, *LinkStack;
// InitStack
void InitStack(LinkStack &S){
S = NULL;
}
// Push
bool Push(LinkStack &S, ElemType e){
LinkStack p = new StackNode;
p->data = e;
p->next = S;
S = p;
return true;
}
// Pop
bool Pop(LinkStack &S, ElemType &e){
if(S == NULL)
return false;
StackNode *p = S;
e = p->data;
S = S->next;
delete p;
return true;
}
// GetTop
ElemType GetTop(LinkStack S){
if(S != NULL)
return S->data;
}
2 队列
顺序队列
// 循环队列
/*
队满:(Q.rear + 1) % MaxSize == Q.front
队空: Q.front == Q.rear
队列中元素个数: (Q.rear + MaxSize - Q.front) % MaxSize
*/
#define MaxSize 50
typedef struct{
ElemType data;
int front,rear;
}SqQueue;
// InitQueue
void InitQueue(SqQueue &Q){
Q.rear = Q.front = 0;
}
// isEmpty
bool isEmpty(SqQueue Q){
if(Q.rear == Q.front)
return true;
else
return false;
}
// EnQueue
bool EnQueue(SqQueue &Q, ElemType e){
if((Q.rear + 1) % MaxSize = Q.front)
return false;
Q.data[Q.rear] = e; // 先进队,再加一
Q.rear = (Q.rear + 1)%MaxSize;
return true;
}
// DeQueue
bool DeQueue(SqQueue &Q, ElemType &e){
// isEmpty(Q)
if(Q.rear == Q.front)
return false;
e = Q.data[Q.front];
Q.front = (Q.front + 1)%MaxSize;
return true;
}
链队
// 链式队列结点
typedef struct {
EelemType data;
struct LinkNode *next;
}LinkNode;
// 链式队列
typedef struct{
LinkNode *rear,*front;
}LinkQueue;
// InitQueue
void InitQueue(LinkQueue &Q){
LinkQueue *rear,*front = new LinkQueue;
// LinkQueue *front = new LinkQueue;
front->next = NULL;
}
// IsEmpty
void EnQueue(LinkQueue Q){
if(Q.front == Q.rear) return true;
else return false;
}
// EnQueue
void EnQueue(LinkQueue &Q, ElemType x){
LinkQueue *s = new LinkQueue;
s->data = x;
s->next = NULL;
Q.rear->next = s;
Q.rear = s;
}
// DeQueue
bool DeQueue(LinkQueue &Q, ElemType &x{
if(Q.front == Q.rear) return false;
LinkQueue *p = new LinkQueue;
x = p->data;
Q.front->next = p->next;
if(Q.rear == p)
Q.rear = Q.front; // 如果只有一个结点,删除后,队列为空
delete p;
return true;
}
3 栈和队列的应用
数制的转换
#include<iostream>
using namespace std;
void conversion(int N){
// 对任意一个非负十进制数,打印输出与其等值的八进制数
int e;
InitStack(S);
while(N){
Push(S, N%8);
N = N/8;
}
while(!StackEmpty(S)){
Pop(S, e);
cout<<e;
}
}
括号匹配
表达式求值
队列在层次遍历中的应用
舞伴问题
三、串与数组广、义表
1 串
串的存储结构
// 定长顺序存储
#define MaxSize
typedef struct{
char ch[MaxSize];
int length;
}SString;
// 堆分配存储
typedef struct{
char *ch; // 按串长分配存储区,ch指向串的基地址
int length;
}HString;
// 串的链式存储结构
#define CHUNKSIZE 80
typedef struct Chunk{
char ch[CHUNKSIZE]; // 结点(块)内数组(大小)
struct Chunk *next; // 链表指针
}Chunk;
typedef struct {
Chunk *head,tail; // 头指针和尾指针
int length;
}LString;
串的模式匹配算法
// 1 暴力匹配算法BF 算法时间复杂度O(mn)
int Index_BF(SString T, SString S, int pos){
// 返回模式T在主串S中第pos个字符起第一次出现的位置。若不存在则返回值为0
int i = pos;
int j = 1;
while(i <= S.length && j <= T.length){ // 两个串均未比较到串尾
if(S.ch[i] == T.ch[j];){
++i;
++j;
}
else{
i = i-j+2;
j = 1;
}
}
if(j > T.length)
return i-T.length;
else
return 0;
}
// 2 KMP算法 算法时间复杂度O(n+m)
int Index_BF(SString T, SString S, int pos){
// 返回模式T在主串S中第pos个字符起第一次出现的位置。若不存在则返回值为0
int i = pos;
int j = 1;
while(i <= S.length && j <= T.length){ // 两个串均未比较到串尾
if(j == 0 || S.ch[i] == T.ch[j];){
++i;
++j;
}
else{
j = next[j];
}
}
if(j > T.length)
return i-T.length;
else
return 0;
}
// 计算next 函数值
void get_next(SString T, int next[]){
int i = 1, j = 0;
int next[1] = 0;
while(j < T.length){
if(j == 0 || T.ch[i] == T.ch[j]]{
++i;
++j;
next[i] = j;
}
else j = next[j];
}
}
// 计算nextval函数值
void get_next(SString T, int next[]){
int i = 1, j = 0;
int nextval[1] = 0;
while(j < T.length){
if(j == 0 || T.ch[i] == T.ch[j]]{
++i;
++j;
if(T.ch[i]!=T.ch[j])
nextval[i] = j;
else
nextval[i] = nextval[j];
}
else j = nextval[j];
}
}
2 矩阵
存储结构(顺序存储、三元组、十字链表)
// 顺序存储
typedef struct
{
ElemType* base; //数组元素基址,由InitArray分配
int dim; //数组维数
int* bounds; //数组维界基址,由InitArray分配
int* constants; // 数组映象函数常量基址,由InitArray分配
} Array;
//三元组
typedef struct {
int row,col;
int data;
}triple;
//三元组顺序表
typedef struct {
triple data[NUM];
int c, r, len;
}TSMatrix;
/*==========================*/
// 十字链表法
typedef struct OLNode
{
int i, j, e; //矩阵三元组i代表行 j代表列 e代表当前位置的数据
struct OLNode* right, * down; //指针域 右指针 下指针
}OLNode, * OLink;
typedef struct
{
OLink* rhead, * chead; //行和列链表头指针
int mu, nu, tu; //矩阵的行数,列数和非零元的个数
}CrossList;
矩阵算法(转置、相加)
- 稀疏矩阵的三种不同的存储方法,采用哪种方法要看程序具体要实现的功能:
- 如果想完成例如矩阵的转置这样的操作,宜采用三元组顺序表;
- 如果想实现矩阵的乘法这样的功能,宜采用行逻辑链接的顺序表;
- 如果矩阵运算过程中(例如矩阵的加法),需要不断地插入非 0 元素或删除变为 0 的元素,宜采用十字链表法。
矩阵的转置
#include<stdio.h>
#define NUM 10
//三元组
typedef struct {
int i, j;
int data;
}triple;
//三元组顺序表
typedef struct {
triple data[NUM];
int mu, nu, tu;
}TSMatrix;
//稀疏矩阵的转置
void transposeMatrix(TSMatrix M, TSMatrix* T) {
//1.稀疏矩阵的行数和列数互换
(*T).mu = M.nu;
(*T).nu = M.mu;
(*T).tu = M.tu;
if ((*T).tu) {
int col, p;
int q = 0;
//2.遍历原表中的各个三元组
for (col = 1; col <= M.nu; col++) {
//重复遍历原表 M.m 次,将所有三元组都存放到新表中
for (p = 0; p < M.tu; p++) {
//3.每次遍历,找到 j 列值最小的三元组,将它的行、列互换后存储到新表中
if (M.data[p].j == col) {
(*T).data[q].i = M.data[p].j;
(*T).data[q].j = M.data[p].
}
}
}
矩阵相加
#include<stdio.h>
#include<stdlib.h>
#define ElemType int
#define MAX_SIZE 101
typedef struct
{
int row;//行下标
int col;//列下标
ElemType value;//元素值
}Triple;
typedef struct
{
int m;//行数
int n;//列数
int t;//非0元素个数
Triple data[MAX_SIZE];
}TMatrix;
void create_matrix(TMatrix &s,int M,int N)//矩阵创建
{
s.m=M;s.n=N;
printf("输入非0元素的个数:");
scanf("%d",&s.t);
for(int i=1;i<=s.t;i++)
{
printf("输入第%d个非0元素的行数、列数以及数值:",i);
scanf("%d%d%d",&s.data[i].row,&s.data[i].col,&s.data[i].value);
}
}
void add_matrix(TMatrix a,TMatrix b,TMatrix &c)//矩阵相加
{
int temp=1;
c.m=a.m;c.n=a.n;
c.t=0;
for(int i=1;i<=a.t;)
for(int j=1;j<=b.t;)
{
if(a.data[i].row>b.data[j].row)
{
c.data[temp].row=b.data[j].row;
c.data[temp].col=b.data[j].col;
c.data[temp].value=b.data[j].value;//小的给到c
c.t++;//非零元素加一
temp++;j++;
}
else if(a.data[i].row<b.data[j].row)
{
c.data[temp].row=a.data[i].row;
c.data[temp].col=a.data[i].col;
c.data[temp].value=a.data[i].value;//小的给到c
c.t++;//非零元素加一
temp++;i++;
}
else //行号相等
{
if(a.data[i].col>b.data[j].col)
{
c.data[temp].row=b.data[j].row;
c.data[temp].col=b.data[j].col;
c.data[temp].value=b.data[j].value;//小的给到c
c.t++;//非零元素加一
temp++;j++;
}
else if(a.data[i].col<b.data[j].col)
{
c.data[temp].row=a.data[i].row;
c.data[temp].col=a.data[i].col;
c.data[temp].value=a.data[i].value;//小的给到c
c.t++;//非零元素加一
temp++;i++;
}
else //列号也相等
{
c.data[temp].row=a.data[i].row;
c.data[temp].col=a.data[i].col;
c.data[temp].value=a.data[i].value+b.data[j].value;//加和并给到c
c.t++;//非零元素加一
temp++;i++;j++;
}
}
}
}
void disp_matrix(TMatrix s)//矩阵显示
{
ElemType A[(s.m)+1][(s.n)+1]={0};//定义二维数组,并使初始值均为0
for(int temp=1;temp<=s.t;temp++)//非0元素进入数组
A[s.data[temp].row][s.data[temp].col]=s.data[temp].value;
for(int i=1;i<=s.m;i++)//显示完整的矩阵
{
for(int j=1;j<=s.n;j++)
printf(" %d",A[i][j]);
printf("\n");
}
}
int main()
{
TMatrix a,b,c;
int M,N;//m:行数 n:列数
printf("输入矩阵行数:");scanf("%d",&M);
printf("输入矩阵列数:");scanf("%d",&N);
printf("创建矩阵a:");create_matrix(a,M,N);
printf("完整的矩阵a:\n");disp_matrix(a);
printf("创建矩阵b:");create_matrix(b,M,N);
printf("完整的矩阵b:\n");disp_matrix(b);
add_matrix(a,b,c);
printf("非零元素矩阵c:非零元素共有%d个\n行下标 列下标 元素值\n",c.t);
for(int i=1;i<=c.t;i++)
printf(" %d %d %d\n",c.data[i].row,c.data[i].col,c.data[i].value);
printf("完整的矩阵c:\n");disp_matrix(c);
return 0;
}
3 广义表
四、树和二叉树
1 二叉树
二叉树的存储结构
// 顺序存储
#define MaxSize 100 // 二叉树最大结点数
typedef TElemType SqBiTree[MaxSize];
SqBiTree bt;
// 链式存储
typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
二叉树递归遍历
#include<iostream>
using namespace std;
typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
// 先序遍历
void PreOrder(BiTree T){
if(T){
cout<<T->data;
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
// 中序遍历
void InOrder(BiTree T){
if(T){
InOrder(T->lcild);
cout<<T->data;
InOrder(T->rchild);
}
}
// 后序遍历
void PostOrder(BiTree T){
if(T){
PostOrder(T->lchild);
PostOrder(T->rchild);
cout<<T->data;
}
}
二叉树非递归遍历
#include<iostream>
using namespace std;
typedef struct BiTNode{
TElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
/*
中序遍历非递归算法
1⃣️ 初始化一个空栈S,指针p指向根结点
2⃣️ 申请一个结点空间q,用来存放栈顶弹出的元素
3⃣️ 当p非空,或者栈S非空时,循环执行一下操作:
- 如果p非空,则将p进栈,p指向该结点的左孩子;
- 如果p为空,则弹出栈顶元素并访问,将p指向该结点的右孩子。
*/
void InOrder(BiTree T){
InitSatck(S); // 初始化栈S
BiTree *p = T; // 指针p初始化指向根结点
BiTree q = new BiTree; // 存放栈顶弹出的元素
while(p || !StackEmpty(S)){ // StackEmpty(S)判断栈是否为空
if(p){ // 若p不空
Push(S,p); // 根指针进栈
p = p->lchild; // 根指针进栈,遍历左子树
}
else{
Pop(S,q); // 退栈
cout<<q->data; // 访问结点值
p = q->rchild; // 遍历右子树
}
}
}
/*
前序遍历非递归算法
1⃣️ 初始化一个空栈S,指针p指向根结点
2⃣️ 当p非空,或者栈S非空时,循环执行一下操作:
- 如果p非空,则访问p结点,将p进栈,p指向该结点的左孩子;
- 如果p为空,则弹出栈顶,将p指向该结点的右孩子。
*/
void InOrder(BiTree T){
InitSatck(S); // 初始化栈S
BiTree *p = T; // 指针p初始化指向根结点
// BiTree q = new BiTree; // 存放栈顶弹出的元素
while(p || !StackEmpty(S)){ // StackEmpty(S)判断栈是否为空
if(p){ // 若p不空
cout<<p->data;
Push(S,p); // 根指针进栈
p = p->lchild; // 根指针进栈,遍历桌子树
}
else{
Pop(S,p); // 退栈
p = p->rchild; // 遍历右子树
}
}
}
/*
后序遍历非递归算法
1⃣️ 初始化一个空栈S,指针p指向根结点
2⃣️ 初始化一个空指针r,指向被访问过的结点
3⃣️ 当p非空,或者栈S非空时,循环执行一下操作:
- 如果p非空,则将p进栈,p指向该结点的左孩子;
- 如果p为空,则取栈顶元素,并判断该元素是否有右孩子,且右孩子是否被访问过:
- 若右孩子存在且未被访问过,则p指向右孩子
- 否则弹出栈顶元素,并访问,并将r指向访问过的p指针指向的结点;p令为空。
*/
void InOrder(BiTree T){
InitSatck(S); // 初始化栈S
BiTree *p = T; // 指针p初始化指向根结点
BiTree *s = NULL; // 存放栈顶弹出的元素
while(p || !StackEmpty(S)){ // StackEmpty(S)判断栈是否为空
if(p){ // 若p不空
Push(S,p); // 根指针进栈
p = p->lchild; // 根指针进栈,遍历左子树
}
else{
GetTop(S,p); // 获取栈顶元素
if(p->rchild && p->rchild!=r){ // 右孩子存在且未被访问过
p = p->rchild;
}
else{
Pop(S,p); // 出栈
cout<<p->data; // 访问出栈结点值
r = p; // r指向被访问过的结点
p = NULL;
}
}
}
}
/*
层序遍历非递归算法
1⃣️ 初始化一个空堆队列Q,
2⃣️ 指针p指向根结点,根结点先入队
3⃣️ 当队Q非空时,循环执行一下操作:
- 对头结点出队,访问该结点的值;
- 如果该结点有左孩子,则左孩子入队;若有右孩子,右孩子入队;
- 若左右孩子都有,先左孩子后右孩子,依次入队
*/
void LevelOrder(BiTree T){
InitQueue(Q); // 初始化队列Q
BiTree *p = T; // 指针p初始化指向根结点
EnQueue(Q, p); // 根结点入队
while(!IsEmpty(Q)){ // IsEmpty(Q)判断队是否为空
DeQueue(Q,p); // 头结点出队
cout<<p->data; // 访问结点值
if(p->lchild!=NULL)
EnQueue(Q,p->lchild); // 访问结点的左孩子不空入队
if(p->rchild!=NULL)
EnQueue(Q,p->rchild); // 访问结点的右孩子不空入队
}
}
2 二叉树遍历算法的应用
先序遍历的顺序建立二叉链表
为了简化问题,设二叉树中的结点的元素均为一个单个字符。从根结点开始,递归创建二叉树。
/*
算法步骤:
1⃣️ 扫描字符序列,读入字符ch
2⃣️ 如果ch是一个#字符,则表明该二叉树为空树,即T= NULL;否则执行以下操作:
- 申请一个结点空间T;
- 将ch赋值给T->data;
- 递归创建T的左子树;
- 递归创建右子树。
*/
typedef struct BiTNode{
TElemtype data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
void CreateBiTree(BiTree &T){
cin>>ch;
if(ch == ‘#’)
T = NULL; // 递归结束,建空树
else{ // 递归创建二叉树
BiTree T = new BiTree; // 生成根结点
T->data = ch;
CreateBiTree(T->lchild); // 递归创建左子树
CreateBiTree(T->rchild); // 递归创建右子树
}
}
复制二叉树
若二叉树不空,先复制根结点,然后复制根结点的左子树和右子树,与二叉树先序遍历的实现非常相似。
/*
算法步骤:
1⃣️ 如果是空树,递归结束;否则执行以下操作:
- 申请一个新结点空间,复制根结点;
- 递归复制左子树;
- 递归复制右子树。
*/
typedef struct BiTNode{
TElemtype data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
bool CopyBiTree(BiTree T,BiTree &NewT){
if(T == NULL ){
NewT = NULL;
return false;
}
else{
BiTree NewT = new BiTree; // 生成根结点
NewT->data = T->data;
CopyBiTree(T->lchild, NewT->lchild); // 递归复制左子树
CopyBiTree(T->rchild, NewT->rchild); // 递归复制右子树
}
return true;
}:
计算二叉树的深度
二叉树的深度为树中结点的最大层次,二叉树的深度为左右子树深度的交大者加1
/*
算法步骤:
1⃣️ 如果二叉树为空树,递归结束,深度为0;否则执行以下操作:
- 递归计算左子树的深度记为m;
- 递归计算右子树的深度记为n;
- 如果m大与n,二叉树深度为m+1,否则为n+1。
*/
typedef struct BiTNode{
TElemtype data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
int Depth(BiTree T){
int m,n = 0;
if(T == NULL){
return 0; // 递归结束,树深0
}
else{
int m = Depth(T->lchild); // 递归计算左子树深度
int n = Depth(T->rchild); // 递归计算右子树深度
if(m > n){
return m+1;
}
else{
return n+1;
}
}
}
计算二叉树中结点的个数
如果是空树,则结点个数为0;否则,结点个数为左子树结点加上右结点的个数再加上1
int CountNode(BiTree T){
if(T == NULL)
return 0;
else
return CountNode(T->lchild)+(T->rchild)+1;
}
3 二叉树算法应用
统计二叉树叶子结点个数
[ 题目分析 ] 如果二叉树为空,返回 0,如果二叉树不为空且左右子树为空,返回 1,如果二叉树不为空,且左右子树不同时为空,返回左子树中叶子节点个数加上右子树中叶子节点个数。
typedef struct BiTNode{
TElemType data;
struct BITNode *lchild, *rchild;
}BiTNode, *BiTree;
int LeafNodeCount(BiTree T)
{
if(T==NULL)
return 0; // 如果是空树,则叶子结点个数为 0
else if(T->lchild==NULL&&T->rchild==NULL)
return 1; // 判断结点是否是叶子结点(左孩子右孩子都为空) ,若是则返回 1
else
return LeafNodeCount(T->lchild)+LeafNodeCount(T->rchild);
}
判断两个树是否相等
int compareTree(TreeNode* tree1, TreeNode* tree2)
//用分治的方法做,比较当前根,然后比较左子树和右子树
{
bool tree1IsNull = (tree1==NULL);
bool tree2IsNull = (tree2==NULL);
if(tree1IsNull != tree2IsNull)
{
return 1;
}
if(tree1IsNull && tree2IsNull)
{// 如果两个都是 NULL ,则相等
return 0;
}// 如果根节点不相等,直接返回不相等,否则的话,看看他们孩子相等不相等
if(tree1->c != tree2->c)
{
return 1;
}
return (compareTree(tree1->left,tree2->left)&compareTree(tree1->right,tree2->right));
// (compareTree(tree1->left,tree2->right)&compareTree(tree1->right,tree2->left));
}// 算法结束
交换二叉树每个结点的左孩子和右孩子
[ 题目分析 ] 如果某结点左右子树为空,返回,否则交换该结点左右孩子,然后递归交换左右子树。
void ChangeLR(BiTree &T)
{
BiTree temp;
if(T->lchild==NULL&&T->rchild==NULL)
return;
else
{
temp = T->lchild;
T->lchild = T->rchild;
T->rchild = temp;
}// 交换左右孩子
ChangeLR(T->lchild); //递归交换左子树
ChangeLR(T->rchild); //递归交换右子树
}
设计二叉树的双序遍历算法
(双序遍历是指对于二叉树的每一个结点来说,先访问这个结点,再按双序遍历它的左子树,然后再一次访问这个结点,接下来按双序遍历它的右子树)。
[ 题目分析 ] 若树为空,返回;若某结点为叶子结点,则仅输出该结点;否则先输出该结点,递归遍历其左子树,再输出该结点,递归遍历右子树。
void DoubleTraverse(BiTree T)
{
if(T == NULL)
return;
else if(T->lchild==NULL&&T->rchild==NULL)
cout<<T->data; // 叶子结点输出
else
{
cout<<T->data;
DoubleTraverse(T->lchild); // 递归遍历左子树
cout<<T->data;
DoubleTraverse(T->rchild); // 递归遍历右子树
}
}
计算二叉树最大的宽度
二叉树的最大宽度是指二叉树所有层中结点个数的最大值
[ 题目分析 ] 求二叉树高度的算法见上题。 求最大宽度可采用层次遍历的方法, 记下各层结点数,每层遍历完毕,若结点数大于原先最大宽度,则修改最大宽度。
/*
算法步骤:
若二叉树为空返回宽度值为0,否则执行下列步骤:
1⃣️ 创建一个队列Q,数据类型为二叉树结点指针
2⃣️ 初始化队列指针(front=1,rear=1,last=1)分别表示对头,队尾,同一层最右结点的位置,并初始化定义宽度变量temp=0为局部宽度!maxs=0为全局最大宽度。
3⃣️ 头结点入队,若对头指针未超过同层最右结点位置(front<=last),则执行以下步骤:
- 对头结点指针出队,temp加一,局部宽度temp加一;
- 判断出队结点是否有左右孩子结点,若有则入队,同时尾指针rear加一
- 若同一层遍历结束(front>last),则更新last(last=rear)开启下一层的遍历,同时更新最大宽度值;若一层未遍历结束,则继续执行3⃣️。
4⃣️ 最后一层遍历结束(也就是3⃣️中所有循环结束),返回最大宽度值,算法结束。
*/
int Width(BiTree bt)// 求二叉树 bt 的最大宽度
{
if (bt==null) return (0); // 空二叉树宽度为 0
else
{
BiTree Q[];//Q 是队列,元素为二叉树结点指针,容量足够大
front=1;rear=1;last=1;
//front 队头指针 ,rear 队尾指针 ,last 同层最右结点在队列中的位置
temp=0; maxw=0; //temp 记局部宽度 , maxw 记最大宽度
Q[rear]=bt; // 根结点入队列
while(front<=last)
{
p=Q[front++]; temp++; // 同层元素数加 1
if (p->lchild!=null) Q[++rear]=p->lchild; // 左子女入队
if (p->rchild!=null) Q[++rear]=p->rchild; // 右子女入队
if (front>last) // 一层结束,
{
last=rear;
if(temp>maxw) maxw=temp;
//last 指向下层最右元素 , 更新当前最大宽度
temp=0;
}//if
}//while
return (maxw);
}
}// 结束 width
用按层次顺序遍历二叉树的方法,统计树中具有度为 1 的结点数目。
[题目分析 ]若某个结点左子树空右子树非空或者右子树空左子树非空,则该结点为度为 1 的结点
/*
算法步骤:
1⃣️ 定义变量num记录度为1的结点个数,若树不空,头结点入队。
2⃣️ 队列不空则,对头结点出队,并判断该结点是否是度为1的结点,若是num++;
3⃣️ 并将该结点的孩子结点入队;
4⃣️ 重复执行2⃣️3⃣️,直到队空,二叉树遍历完成,返回度为1的结点数(num),算法结束。
*/
int Level(BiTree bt) // 层次遍历二叉树,并统计度为 1 的结点的个数
{
int num=0; //num 统计度为 1 的结点的个数
if(bt)
{
QueueInit(Q); QueueIn(Q,bt);//Q 是以二叉树结点指针为元素的队列
while(!QueueEmpty(Q))
{
p=QueueOut(Q); cout<<p->data; // 出队 , 访问结点
if(p->lchild && !p->rchild ||!p->lchild && p->rchild) num++;
// 度为 1 的结点
if(p->lchild) QueueIn(Q,p->lchild); // 非空左子女入队
if(p->rchild) QueueIn(Q,p->rchild); // 非空右子女入队
} //while(!QueueEmpty(Q))
}//if(bt)
return(num);
}// 返回度为 1 的结点的个数
求任意二叉树中第一条最长的路径长度,并输出此路径上各结点的值。
[ 题目分析 ] 因为后序遍历栈中保留当前结点的祖先的信息,用一变量保存栈的最高栈顶指针,每当退栈时,栈顶指针高于保存最高栈顶指针的值时,则将该栈倒入辅助栈中,辅助栈始终保存最长路径长度上的结点,直至后序遍历完毕,则辅助栈中内容即为所求。
**未完待续**
void LongestPath(BiTree bt)// 求二叉树中的第一条最长路径长度
{
BiTree p=bt,l[],s[];
//l, s 是栈,元素是二叉树结点指针, l 中保留当前最长路径中的结点
int i , top=0,tag[],longest=0;
while(p || top>0)
{
while(p) {
s[++top]=p;
tag[top]=0;
p=p->Lc;
} // 沿左分枝向下
if(tag[top]==1) // 当前结点的右分枝已遍历
{
if(!s[top]->Lc && !s[top]->Rc) // 只有到叶子结点时,才查看路径长度
if(top>longest)
{
for(i=1;i<=top;i++) l[i]=s[i]; longest=top; top--;
}
// 保留当前最长路径到 l 栈,记住最高栈顶指针,退栈
}
else if(top>0) {tag[top]=1; p=s[top].Rc;} // 沿右子分枝向下
}//while(p!=null||top>0)
}// 结束 LongestPath
输出二叉树中从每个叶子结点到根结点的路径。
[ 题目分析 ] 采用先序遍历的递归方法, 当找到叶子结点 *b 时,由于 *b 叶子结点尚未添加到 path 中,因此在输出路径时还需输出 b->data 值
void AllPath(BTNode *b,ElemType path[],int pathlen)
{
int i;
if (b!=NULL)
{
if (b->lchild==NULL && b->rchild==NULL) //*b 为叶子结点
{
cout << " " << b->data << " 到根结点路径 :" << b->data;
for (i=pathlen-1;i>=0;i--)
cout << endl;
}
else
{
path[pathlen]=b->data; // 将当前结点放入路径中
pathlen++; //路径长度增 1
AllPath(b->lchild,path,pathlen); // 递归扫描左子树
AllPath(b->rchild,path,pathlen); // 递归扫描右子树
pathlen--; //恢复环境
}
}//if (b!=NULL)
}// 算法结束
五、图
1 邻接矩阵存储表示
邻接矩阵的优缺点
优点:
- 便于判断两个顶点时间是否有边。
- 便于计算各个顶点的度。
缺点:
- 不便增加和删除结点。
- 不便统计边的数目,时间复杂度O(n^2)。
- 空间复杂度高。
#define MaxInt 32767 // 表示极大值,∞
#define MVNum 100 // 最大顶点数
typedef char VerTexType; // 假设顶点的数据类型为字符型
typedef int ArcType; // 假设边的权值类型为整型
typedef struct{
VerTexType vexs[MVNum]; // 顶点表
ArcType arcs[MVNum][MVNum]; // 邻接矩阵
int vexnum,arcnum; // 图的当前顶点数和边数
}AMGraph;
2 邻接表存储表示
邻接表的优缺点
优点:
- 便于判断两个顶点时间是否有边。
- 便于计算各个顶点的度。
缺点:
- 不便增加和删除结点。
- 不便统计边的数目,时间复杂度O(n^2)。
- 空间复杂度高。
#define MVNum 100
// 边结点
typedef struct ArcNode{
int adjvex; // 该边所指向的顶点的位置
struct AtcNode *next; // 指向下一条边的指针
OtherInfo info; // 和边相关的信息
}ArcNode;
// 顶点表结点
typedef struct VNode{
VerTexType data; // 顶点信息
ArcNode *firstarc; // 指向第一条依附该顶点的边指针
}VNode,AdjList[MVNum]; // 邻接表类型
typedef struct{
AdjList vertices; // 邻接表
int vexnum,arcnum; // 图的顶点数和弧数
}ALGraph; // 以邻接表存储的图类型
3 深度优先搜索遍历
为了在遍历过程中区分顶点是否已被访问过,需附设访问标志数组visited[n],其初值为false,一旦被分文过,则其相应的分量置为true。
// 1 深度优先搜索遍历连通图
/*
算法步骤:
1. 从图中的某个顶点v出发,访问v,并将visited[v]置为true;
2. 依次检查v的所有邻接点w,如果visited[w]的值为false,再从w进行递归遍历,直至所有结点都被访问过。
*/
#define MVNum 100;
bool visited[MVNum];
void DFS(Graph G, int v){
cout<<v;
visited[v] = true; // 访问v,并置visited[v]为true
for(int w = FirstAdjVex(G,v); w >= 0; w = NextAdjVex(G,v,w)){
// FirstAdjVex(G,v) 表示v的第一个邻接顶点
// NextAdjVex(G,v,w) 表示v相对于w来说下一个邻接点,w>=0 表示w存在
if(!visited[w])
DFS(G,w); // 如果w为被访问过,递归调用DFS
}
}
// 2 深度优先搜索遍历非连通图
void BFSTraverse(G){
for(int v = 0; v < G.vexnum; v++)
visited[v] = false; // 初始化visited数组
for(int v = 0; v < G.vexnum; v++)
DFS(G,v); // 对尚未访问的结点,递归调用DFS
}
上述1和2算法是深度优先搜索遍历图的算法,但是其中FirstAdjVex(G,v)和NextAdjVex(G,v,w)没有具体展开。如果对于不同的存储结构,实现的方法不同,耗时也不同。
采用邻接矩阵表示图的深度优先搜索遍历
#include<iostream>
using namespce std;
#define MVNum 100; // 顶点的最大值
typedef char VerTexType; // 假设顶点的数据类型为char
typedef int ArcType; // 假设边的数据类型为int
typedef struct{
VerTexType vexs[MVNum]; // 顶点表
int arcs[MVNum][MVNum]; // 邻接矩阵
int vernum,arcnum; // 顶点数和边数
}ANGraph;
bool visited[MVNum]; // 访问标志数组
void DFS_AM(AMGraph G, int v){
cout<<v;
visited[v] = true; // 访问v,并置visited[v]为true
for(int w = 0; w < G.vernum; w++){ // 依次检查v所在的行
if((G.arcs[v][w]!=0) && (!visited[w]))
DFS_AM(G,w); // 如果w是v的邻接点且w未被访问过,则递归DFS_AM,直至所有点都被访问过
}
}
// 如果图不是连通图,对非连通图做深度优先搜素遍历
void DFSTraverse(Graph G){
for(int v = 0; v < G.vernum; v++)
visited[v] = false; // 初始化访问标志数组
for(int v = 0; v < G.vernum; v++)
DFS_AM(G,V); // 对尚未访问的顶点调用DFS_AM
}
int main(){
AMGraph G = new AMGraph; // 定义一个图结构
CreateGraph(G); // 创建一个图
DFSTraverse(G); // 深度优先搜索遍历
}
采用邻接表表示图的深度优先搜索遍历
#include<iostream>
using namespce std;
#define MVNum 100; // 顶点的最大值
typedef char VerTexType; // 假设顶点的数据类型为char
bool visited[MVNum]; // 访问标志数组
// 边结点
typedef srtuct ArcNode{
int adjvex; // 指向该边所指的结点
struct ArcNode *nextarc; // 指向下一条依附该顶点的边
}ArcNode;
// 顶点表结点
typedef struct VNode{
VerTexType data; //顶点信息
struct ArcNode *firstarc; // 依附该顶点的第一条边
}VNode,AdjList[MVnum]; // 表示邻接表类型
// 邻接表
typedef struct{
AdjList vertices;
int vernum,arcnum; // 当前顶点数和边数
}ALGraph;
void DFS_AL(ALGraph G, int v){
cout<<v;
visited[v] = true; // 访问v,并置visited[v]为true
ALGraph *p = G.verstics[v].firstarc; // p指向v的链表的第一个系欸DNA
while(p!=NULL){
int w = p->adjvex; // 表示w是v的邻接点
if(!visited[w])
DFS_AL(G,w); // 如果w是v的邻接点且w未被访问过,则递归DFS_AL,直至所有点都被访问过
p = p->nextarc; // 指向下一个边结点
}
}
// 如果图不是连通图,对非连通图做深度优先搜素遍历
void DFSTraverse(Graph G){
for(int v = 0; v < G.vernum; v++)
visited[v] = false; // 初始化访问标志数组
for(int v = 0; v < G.vernum; v++)
DFS_AM(G,V); // 对尚未访问的顶点调用DFS_AM
}
int main(){
AMGraph G = new AMGraph; // 定义一个图结构
CreateGraph(G); // 创建一个图
DFSTraverse(G); // 深度优先搜索遍历
}
4 广度优先搜索遍历
/*
算法描述:
1. 从图中某个顶点v出发,访问v,并置visited[v]的值为true,然后将v进队。
2. 只要队头不空,则重复下述操作:
- 队头顶点u出队;
- 一次检查u的所有邻接点w,如果visited[w]的值为false,则访问w,并置visited[w]的值为true,然后将w入队。
*/
void BFS(Graph G, int v){
cout<<v;
visited[v] = true; // 访问v,并置visited[v]为true
InitQueue(Q); // 初始化队列Q
EnQueue(Q,v); // v进队
}