数据结构

@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;


矩阵算法(转置、相加)

  • 稀疏矩阵的三种不同的存储方法,采用哪种方法要看程序具体要实现的功能:
  1. 如果想完成例如矩阵的转置这样的操作,宜采用三元组顺序表;
  2. 如果想实现矩阵的乘法这样的功能,宜采用行逻辑链接的顺序表;
  3. 如果矩阵运算过程中(例如矩阵的加法),需要不断地插入非 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 广义表

广义表的结构以及广义表在M元多项式中的应用

四、树和二叉树

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 邻接矩阵存储表示

邻接矩阵的优缺点

优点:

  1. 便于判断两个顶点时间是否有边。
  2. 便于计算各个顶点的度。

缺点:

  1. 不便增加和删除结点。
  2. 不便统计边的数目,时间复杂度O(n^2)。
  3. 空间复杂度高。
#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 邻接表存储表示

邻接表的优缺点

优点:

  1. 便于判断两个顶点时间是否有边。
  2. 便于计算各个顶点的度。

缺点:

  1. 不便增加和删除结点。
  2. 不便统计边的数目,时间复杂度O(n^2)。
  3. 空间复杂度高。
#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进队
}