线性表
顺序表
初始化
静态数组
#define MaxSize 10
typedef struct{
int data[MaxSize];
int length;
}SqList;
void InitList(SqList &L){
for(int i=0; i<MaxSize; i++)
L.data[i]=0;
L.length=0;
}
int main(){
SqList L;
InitList(L);
//......
return 0;
}
动态数组
#define InitSize 100
typedef struct{
int *data;
int MaxSize;
int length;
}SeqList;
void InitList(SeqList &L){
L.data=(int *)malloc(sizeof(int)*InitSize);
L.MaxSize=InitSize;
L.length=0;
}
//增加动态数组的长度
void IncreaseSize(SeqList &L, int len){
int *p=L.data;
L.data=(int *)malloc(sizeof(int)*(InitSize+len));
for(int i=0; i<L.length; i++){
L.data[i]=p[i];
}
L.MaxSize=L.MaxSize+len;
free(p);
}
int main(){
SeqList L;
InitList(L);
//......
IncreaseSize(L, 5);
return 0;
}
C的初始动态分配语句为:
L.data=(ElemType*)malloc(sizeof(ElemType)*InitSize);
C++的初始化分配语句为:
L.data=new ElemType[InitSize];
插入操作
#define MaxSize 10
typedef struct{
int data[MaxSize];
int length;
}SqList;
bool ListInsert(SqList &L, int i, ElemType e){
if (i<1||i>L.length+1)
return false;
if (L.length>=MaxSize)
return false;
for (int j=L.length; j<=i; j--)
L.data[j]=L.data[j-1];
L.data[i-1]=e;
L.length++;
return true;
}
int main(){
SqList L;
InitList(L);
//......
ListInsert(L, 3, 3);
return 0;
}
删除操作
bool ListDelete(SqList &L, int i, ElemType &e){
if(i<1||i>L.length)
return false;
e=L.data[i-1];
for(j=i; j<L.length; j++)
L.data[j-1]=L.data[j];
L.length--;
return true;
}
int main(){
SqList L;
InitList(L);
//......
int e=-1;
if(ListDelete(L,3,e))
print("已删除第3个元素,删除元素值为=%d\n",e);
else
print("位序i不合法,删除失败\n");
return 0;
}
查找操作
按位查找(静态数组)
#define MaxSize 10
typedef struct{
int data[MaxSize];
int length;
}SqList;
ElemType GetElem(SqList L, int i){
return L.data[i-1];
}
按位查找(动态数组)
#define InitSize 100
typedef struct{
int *data;
int MaxSize;
int length;
}SeqList;
ElemType GetElem(SeqList L, int i){
return L.data[i-1];
}
按值查找
#define InitSize 100
typedef struct{
int *data;
int MaxSize;
int length;
}SeqList;
int LocateElem(SeqList L, ElemType e){
for(init i=0; i<length; i++)
if(L.data[i]==e)
return i+1;
return 0;
}
单链表
初始化
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
//typedef关键字:数据类型重命名
struct LNode{
ElemType data;
struct LNode *next;
};
//表示一个单链表,两种方法
LNode *L; //强调是一个单链表
LinkList L; //强调是一个结点
不带头结点的单链表
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
bool InitList(LinkList &L){
L=NULL;
return true;
}
void test(){
LinkList L;
InitList(L);
//......
}
bool Empty(L){
if(L==NULL)
return true;
else
return false;
}
带头结点的单链表
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode, *LinkList;
bool InitList(LinkList &L){
L=(LNode *)malloc(sizeof(LNode));
if(L=NULL)
return false;
L->next=NULL;
return true;
}
void test(){
LinkList L;
InitList(L);
//......
}
bool Empty(L){
if(L->next==NULL)
return true;
else
return false;
}
插入操作
按位序插入(带头结点)
bool LinkList(LinkList L, int i, ElemType e){
if(i<1)
return false;
LNode *p;
int j=0; //j表示当前p指向的是第几个结点
p=L;
while(p!=NULL && j<i-1){ //循环找到第i-1个结点,p指向它
p=p=>next;
j++;
}
if(p==NULL)
return false;
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
按位序插入(不带头结点)
bool ListInsert(LinkList &L, int i, ElemType e){
if(i<0)
return false;
if(i=1){
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=L;
L=s;
return true;
}
LNode *p;
int j=1;
p=L;
while(p!=NULL && j<i-1){
p=p->next;
j++
}
//以下等同于:return InsertNextNode(p, e);
if(p==NULL)
return false;
LNode *s=(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p-<next=s;
return true;
}
指定结点的后插操作
bool InsertNextNode(LNode *p, ElemType e){
if(p==NULL)
return true;
LNode *s=(LNode *)malloc(sizeof(LNode));
if(s==NULL) //内存分配失败
return false;
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
指定结点的前插操作
//后插操作后,交换数据
bool InsertPriorNode(LNode *p, ElemType e){
if(p==NULL)
return true;
LNode *s=(LNode *)malloc(sizeof(LNode));
if(s==NULL)
return false;
s->next=p->next;
p->next=s;
s->data=p->data;
p->data=e;
return true;
}
//插入的是结点
bool InsertPriorNode(LNode *p,LNode *s){
if(p==NULL || s=NULL)
return false;
s->next=p->next;
p->next=s;
ElemType temp=p->data;
p->data=s->data;
s->data=temp;
return true;
}
删除操作
按位序删除(带头结点)
bool ListDelete(LinkList &L, int i, ElemType &e){
if(i<0)
return false;
LNode *p;
int j=0;
p=L;
while(p!=NULL && j<i-1){
p=p->next;
j++;
}
if(p==NULL)
return false;
if(p->next==NULL)
return false;
LNode *q=p->next; //要删除的结点q
e=q->data;
p->next=q->next;
free(q);
return true;
}
指定结点的删除
//此代码在p->next为NULL时,有问题
//其他方法:遍历找前驱,或用双链表
bool DeleteNode(LNode *p){
if(p==NULL)
return false;
LNode *q=p->next;
p->data=q->data; //q为NULL时,会出现空指针
p->next=q->next;
free(p);
return true;
}
查找操作
按位查找
LNode *GetElem(LinkList L, int i){
if(i<0)
return false;
LNode *p;
int j=0;
p=L;
while(p!=NULL && j<i){
p=p->next;
j++;
}
return p;
}
按值查找
LNode *LocateElem(LinkList L, ElemType e){
LNode *p=L->next;
while(p!=NULL && p->data!=e)
p=p->next;
return p;
}
求表长度
int Length(LinkList L){
int len=0;
LNode *p=L;
while(p->next!=NULL){
p=p->next;
len++;
}
return len;
}
单链表建立
尾插法建立单链表
LinkList List_TailInsert(LinkList &L){
int x; //ElemType类型为int
L=(LNode *)malloc(sizeof(LNode));
LNode *s,*r=L; //r为表尾指针
scanf("%d",&x);
while(x!=9999){ //输入9999表示结束
s=(LNode *)malloc(sizeof(LNode));
s->data=x;
r->next=s;
r-s;
scanf("%d",&x);
}
r->next=NULL;
return L;
}
头插法建立单链表(链表的逆置)
LinkList List_HeadInsert(LinkList &L){
int x;
L=(LNode *)malloc(sizeof(LNode));
L->next=NULL; //头插法必须初始未空链表
LNode *s;
scanf("%d",&x);
while(x!=9999){
s=(LNode *)malloc(sizeof(LNode));
s->data=x;
s->next=L->next;
L->next=s;
scanf("%d",&x);
}
return L;
}
双链表
初始化
typedef struct DNode{
ElemType e;
struct DNode *prior,*next;
}DNode,*DLinkList;
插入操作
指定结点的后插操作
bool InsertNextDNode(DNode *p, DNode *s){
if(p==NULL && s==NULL)
return false;
s->next=p->next;
if(p->next!=NULL) //p->next==NULL时,不用指定其前驱结点
p->next->prior=s;
s->prior=p;
p->next=s;
return true;
}
删除操作
删除p结点的后继节点
bool DeleteNextDNode(DNode *p){
if(p==NULL)
return false;
LNode *q=p->next;
if(q==NULL)
return false;
p->next=q->next;
if(q->next!=NULL)
q->next->prior=p;
free(q);
return true;
}
//销毁链表
void DestoryList(DLinkList &L){
while(L->next!=NULL)
DeleteNextDNode(L);
free(L);
L=NULL;
}
遍历操作
//后向遍历
while(p!=NULL){
p=p->next;
}
//前向遍历
while(p!=NULL){
p=p->prior;
}
//前向遍历(跳过头结点)
while(p->prior!=NULL){
p=p->prior;
}
循环链表
循环单链表
typedef struct LNode{
ElemType data;
struct LNode *next;
}LNode,*LinkList;
bool InitList(LinkList &L){
L=(LNode *)malloc(sizeof(LNode));
if(L==NULL)
return false;
L->next=L;
return true;
}
bool Empty(LinkList L){
if(L->next==L)
return true;
else
return false;
}
//判断结点p是否为表尾结点
bool isTail(LinkList L, LNode *p){
if(p->next==L)
return true;
else
return false;
}
循环双链表
typedef struct DNode{
ElemType data;
struct DNode *prior,*next;
}DNode,*DLinkList;
//初始化
bool InitDListLink(DLinkList &L){
L=(DNode *)malloc(sizeof(DNode));
if(L==NULL)
return false;
L->next=L;
L->prior=L;
return true;
}
//在p结点后插入s结点
bool InsertNextDNode(DNode *p, DNode *s){
if(p==NULL && s==NULL)
return false;
s->next=p->next;
p->next->prior=s; //相比双链表,一定有后继节点
s->prior=p;
p->next=s;
return true;
}
//删除p的后继节点
bool DeleteNextDNode(DNode *p){
if(p==NULL)
return false;
LNode *q=p->next;
if(q==NULL)
return false;
p->next=q->next;
q->next->prior=p;
free(q);
return true;
}
静态链表
初始化
#define MaxSize 10
struct Node{
ElemType data;
int next;
}
#define MaxSize 10
typedef struct Node{
ElemType data;
int next;
}SLinkList[MaxSize];
栈和队列
栈(顺序)
初始化
#define MaxSize
typedef struct{
ElemType data[MaxSize];
int top; //栈顶指针(一般表示下标,也可能是位序)
}SqStack;
void InitStack(SqStack &S){
S.top=-1;
//S.top=-1;表示指向栈顶元素,即下标
//S.top=0;表示指向接下来插入的位置,即位序
}
void testStack{
SqStack s;
InitStack(S);
//......
}
book Empty(SqStack S){
if(S.top==-1)
return true;
else
return false;
}
进栈操作
bool Push(SqStack &S, ElemType e){
if(S.top==MaxSize-1)
return false;
S.top=S.top+1;
S.data[S.top]=x; //S.data[++S.top]=x;
return true
}
出栈操作
bool Pop(SqStack &S, ElemType &e){
if(S.top==-1)
return false;
x=S.data[S.top];
S.top=S.top-1; //x=S.data[S.top--];
return true;
}
//读栈顶元素
bool GetTop(SqStack S, ElemType &e){
if(S.top==-2)
return false;
x=S.data[S.top];
return true;
}
共享栈
初始化
#define MaxSize 10
typedef struct{
ElemType e;
int top0;
int top1;
}SqStack;
void InitStack(SqStack &S){
S.top0=-1;
S.top1=MaxSize;
}
链栈(链式)
- 链栈的出队和入队,都是在头部进行
队列(顺序)
初始化
#deine MaxSize 10
typedef struct{
ElemType [MaxSize];
int front,rear;
}
bool InitQueue(SqStack &Q){
Q.rear=Q.front=0;
//队头:指向当前队头元素
//队尾:指向下一个插入的位置
}
void testQueue(){
SqQueue Q;
InitQueue(Q);
//......
}
bool QueueEmpty(SqQueue Q){
if(Q.rear==Q.front)
return true;
else
return false;
}
入队操作
//不合适的写法,因为队头元素会出队
bool EnQueue(SqQueue &Q, ElemType x){
if(rear==MaxSize)
return false;
Q.data[Q.rear]=x;
Q.rear=Q.rear+1;
return true;
}
//循环队列
bool EnQueue(SqQueue &Q, ElemType x){
if((Q.rear+1)%MaxSize==Q.front) //取模,会浪费一个结点
return false;
Q.data[Q.rear]=x;
Q.rear=(Q.rear+1)%MaxSize;
return true;
}
出队操作
//循环队列
bool DeQueue(SqQueue &Q, ElemType &x){
if(Q.rear==Q.front)
return false;
x=Q.data[Q.front];
Q.front=(Q.front+1)%MaxSize;
return true;
}
//获得队头元素
bool DeQueue(SqQueue &Q, ElemType &x){
if(Q.rear==Q.front)
return false;
x=Q.data[Q.front];
return true;
}
判断队满/队空
//方案一:
(Q.rear+1)%MaxSize==Q.front; //队满
Q.rear==Q.front; //队空
(Q.rear-Q.front+MaxSize)%MaxSize; //队列元素个数
//方案二:
typedef struct{
ElemType data[MaxSize];
int front,rear;
int size; //队列当前长度,初始化为0
}
size==MaxSize; //队满
size==0; //队空
//方案三:
typedef struct{
ElemType data[MaxSize];
int front,rear;
int tag; //最近操作,删除为0/插入为1
}
Q.front==Q.rear && tag==1; //队满
Q.front==Q.rear && tag==0; //队空
队列(链式)
初始化
带头结点
typedef struct LinkNode{
ElemType data;
struct LinkNode *next
}LinkNode;
typedef struct{
LinkNode *fornt, *next;
}LinkQueue;
void InitQueue(LinkQueue &Q){
Q.fornt=Q.rear=(LinkNode *)malloc(sizeof(LinkNode));
Q.front->next=NULL;
}
void testLinkQueue{
LinkQueue Q;
InitQueue(Q);
//......
}
bool isEmpty(LinkQueue Q){
if(Q.front==Q.rear)
return true;
else
return false;
}
不带头结点
void InitQueue(LinkQueue &Q){
Q.front=NULL;
Q.rear=NULL;
}
book isEmpty(LinkQueue Q){
if(Q.front==NULL)
return true;
else
return false;
}
入队操作
带头结点
void EnQueue(LinkQueue &Q, ElemType x){
LinkNode *s=(LinkNode *)malloc(sizeof(LinkNode));
s->data=x;
s->next=NULL;
Q.rear->next=s;
Q.rear=s;
}
不带头结点
void EnQueue(LinkQueue &Q, ElemType x){
LinkNode *s=(LinkNode *)malloc(size(Linkode));
s->data=x;
s->next=NULL;
if(Q.front==NULL){ //特殊处理,空队列差入第一个
Q.front=s;
Q.rear=s;
} else {
Q.rear->next=s;
Q.rear=s;
}
}
出队操作
带头结点
bool DeQueue(LinkQueue &Q, ElemType &x){
if(Q.rear==Q.rear)
return false;
LinkNode *p=Q.front->next;
x=p->data;
Q.front->next=q->next;
if(Q.rear==p) //为最后一个结点出队
Q.rear=Q.front;
free(p);
return true;
}
不带头结点
bool DeQueue(LinkQueue &Q, ElemType &x){
if(Q.front==NULL)
return false;
LinkNode *p=Q.front;
x=p->data;
Q.front=p->next;
if(Q.rear==p){
Q.front=NULL;
Q.rear=NULL;
}
free(p);
return true;
}
双端队列
括号匹配问题
#define struct{
char data[MaxSize];
int top;
}SqStack;
//初始化
void InitSatck(SqStack &S);
//判断栈空
bool StackEmpty(SqStack S);
//入栈
bool Push(SqStack &S, char x);
//出栈
bool Pop(SqStack &S, char &x);
bool brackCheck(char str[], int length){
SqStack S;
InitStack(S);
for(int i=0; i<length; i++){
if(str[i]=='(' || str[i]=='[' || str[i]=='{'){
Push(S, str[i]);
} else {
if(StackEmpty(S))
return false;
char topElem;
Pop(S, topElem);
if(str[i]=='(' && topElem!=')')
return false;
if(str[i]=='[' && topElem!=']')
return false;
if(str[i]=='{' && topElem!='}')
return false;
}
}
return StackEmpty(S);
}
表达式求值
后缀表达式
中缀转后缀,左优先原则;
扫描后缀表达式,从左到右;
先弹出“右操作数”
前缀表达式
中缀转前缀,右优先原则;
扫描前缀表达式,从右到左;
先弹出“左操作数”
递归
队列的应用
树的层次遍历
图的广度优先遍历
矩阵的压缩存储
对称矩阵
三角矩阵
三对角矩阵
稀疏矩阵
串
初始化
顺序存储
//静态数组实现
#define MAXLEN 255
typedef struct{
char ch[MAXLEN];
int length;
}SString;
//动态数组实现
typedef struct{
char *ch;
int length;
}HString;
HString S;
S.ch=(char *)malloc(sizeof(char));
S.length=0;
链式存储
//存储密度低:每个字符1B,每个指针4B
typedef struct StringNode{
char ch;
struct StringNode *next;
}StringNode, *String;
typedef struct StringNode{
char ch[4];
struct StringNode *next;
}StringNode, *String;
求子串
#define MAXLEN 255
typedef struct{
char ch[MAXLEN];
int length;
}SString;
//存储方式:数组下标为0的不存储数据,从1开始
bool SubString(SString &Sub, SString S, int pos, int len){
if(pos+lem-1 > S.length)
return false;
for(int i=pos; i<pos+len; i++)
Sub.ch[i-pos+1]=S.ch[i]; //i-pos+1就是从1开始
Sub.length=len;
return true;
}
比较串的大小
//S>T,返回值>0;S=T,返回值>0;S<T,返回值<0
int StringCompare(SString S, SString T){
for(int i=1; i<=S.length && i<=T.length; i++){
if(S.ch[i]!=T.ch[i])
return S.ch[i]-T.ch[i];
}
return S.length-T.length; //全扫描完,比较长度
}
定位操作
int Index(SString S, SString T){
int i=1, n=StrLength(S), m=StrLength(T);
SString sub;
while(i<n-m+1){
SubString(sub, S, i, m); //求子串
if(StrCompare(sub, T)!=0)
i++;
else
return i;
}
return 0; //匹配失败
}
朴素模式匹配
同定位算法(蛮力法)
int Index(SString S, SString T){
int i=1, j=1;
while(i<=S.length && j<=T.length){
if(S.ch[i]==T.ch[j]){
i++;
j++;
} else {
i=j-i+2;
j=1;
}
}
if(j>T.length)
return i-T.length; //此时j为T.length+1
else
return 0;
}
KMP
主串指针不回溯
int Index(SString S, SString T, int nest[]){
int i=1, 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数组:next[1]=0;next[2]=1
next数组优化:next[j]修正为next[next[j]],直至两者不相等
树与二叉树
初始化
顺序存储
#define MaxSize 100
struct TreeNode{
ElemType value;
bool isEmpty;
};
TreeNode t[MaxSize];
链式存储
struct ElemType{
int value;
};
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
//定义一棵树
BiTree root=NULL;
//插入根结点
root =(BiTree)malloc(sizeof(BiTNode));
root->data={1};
root->lchild=NULL;
root->rchild=NULL;
//插入新结点
BiTNode *p=(BiTNode)malloc(sizeof(BiTNode));
p->data={2};
p->lchild=NULL;
p->rchild=NULL;
root->lchild=p;
//带父节点指针
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild, *rchild;
struct BiTNode *parent;
}BiTNode, *BiTree;
遍历操作
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild,*rchild;
}BiTNode, *BiTree;
//先序遍历
void PreOrder(BiTree T){
if(T!=NULL){
visit(T);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
//中序遍历
void InOrder(BiTree T){
if(T!=NULL){
PreOrder(T->lchild);
visit(T);
PreOrder(T->rchild);
}
}
//后序遍历
void PostOrder(BiTree T){
if(T!=NULL){
PreOrder(T->lchild);
PreOrder(T->rchild);
visit(T);
}
}
-
二叉树的遍历(手算):
- 先序遍历:第一遍路过时访问
- 中序遍历:第二遍路过时访问
- 后序遍历:第三遍路过时访问
-
求树的深度
int treeDepth(BiTree T){
if(T=NULL){
return 0;
} else {
int l=treeDepth(T->lchild);
int r=treeDepth(T->rchild);
return l>r ? l+1 : r+1;
}
}
层序遍历
//二叉树的结点(链式存储)
typedef struct BiTNode{
char data;
struct BiTNode *lchild, *rchild;
}
//链式队列结点
typedef struct LinkNode{
BiTNode *data;
struct LinkNode *next;
}LinkNode;
typedef struct{
LinkNode *front,*rear;
}LinkQueue;
//层序遍历
void LevelOrder(BiTree T){
LinkQueue Q;
InitQueue(Q);
BiTree p;
EnQueue(Q, T); //根结点入队
while(!isEmpty){
DeQueue(Q, p);
visit(Q, p);
if(p->lchild!=NULL)
EnQueue(Q, p->lchild);
if(p->rchild!=NULL)
EnQueue(Q, p->rchild);
}
}
构造二叉树
一个二叉树的的中序遍历序列是唯一的
但是只给出一个前序/中序/后序/层序遍历序列,可能对应多个二叉树
唯一确定:要有两种序列,且必须有中序遍历序列
线索二叉树
考点:手画线索二叉树
-
普通二叉树找到指定结点p在中序遍历序列中的前驱/后继:
从根结点出发,重新进行一次中序遍历,指针q记录当时访问的结点,指针pre记录上一个被访问的结点。
①q==p时,pre为前驱
②pre==p时,q为后继
-
n个结点的二叉树,有n+1个空链域
左孩子—前驱线索、右孩子—后继线索
//线索二叉树结点
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild, *rchild;
int ltag,rtag; //tag==0指向孩子,tag==1指向线索
}ThreadNode, *ThreadTree;
中序线索化
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild, *rchild;
int ltag,rtag;
}ThreadNode, *ThreadTree;
ThreadNode *p=NULL; //全局变量,可修改
void CreateInThread(ThreadTree T){
pre=NULL;
if(T!=NULL){
InThread(T);
if(pre->rchild==NULL) //这里pre->rchild一定为NULL
pre->rtag=1;
}
}
//中序遍历二叉树,一遍遍历一遍线索化
void InThread(ThreadTree T){
if(T!=NULL){
InThread(T->lchild);
visit(T);
InThread(T->rchild);
}
}
void visit(ThreadNode *q){
if(q->lchild==NULL){ //pre==NULL,建立前驱线索
q->lchild=pre;
q->ltag=1;
}
if(pre!=NULL && pre->rchild==NULL){ //q==NULL,建立后继线索
pre->rchild=q;
pre->rtag=1;
}
pre=q;
}
//中序线索化
void InThread(ThreadTree p, ThreadTree &pre){ //引用类型,可修改
if(p!=NULL){
InTread(p->lchild, pre);
if(p->lchild==NULL){
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL && pre->rchild==NULL){
pre->rchild=P;
pre->rtag=1;
}
pre=p;
InTread(p->rchild, pre);
}
}
先序线索化
类似于中序线索化,但要特殊处理PreThread(T->ltag)
//原本的PreThread函数中
PreThread(T->ltag);
//更改为:增加一个是否为前驱线索的判断,否则会陷入循环
if(T->ltag==0)
PreThread(T->lchild);
后序线索化
不会出现先序那种转圈的情况,同中序线索化
找前驱后继
中序线索二叉树 找后继
//找到以p为根的子树中,第一个被中序遍历的结点
ThreadNode *Firstnode(ThreadNode *p){
while(p->ltag==0)
p=p->lchild;
return p;
}
//中序线索二叉树中,找到结点p的后继
ThreadNode *Nextnode(ThreadNode *p){
if(rtag==1)
return Firstnode(p->rchild);
else //已线索化
return p->rchild;
}
//补充:对中序线索二叉树,进行中序遍历(非递归算法)
void Inorder(ThreadNode *T){
for(ThreadNode *p=Firstnode(T); p!=NULL; p=Nestnode(p)) //从第一个被中序遍历的结点开始,再依次找到后继访问
visit(p);
}
中序线索二叉树 找前驱
//找到以p为根的子树中,最后一个被中序遍历的结点
ThreadNode *Lastnode(ThreadNode *p){
while(p->rtag==0)
p=p->rchild;
return p;
}
//中序线索二叉树中,找到结点p的前驱
ThreadNode *Prenode(ThreadNode *p){
if(ltag==1)
return Lastnode(p->lchild);
else
return p->lchild;
}
//补充:逆序中序遍历
void RevInorder(ThreadNode *T){
for(ThreadNode *p=Lastnode(T); p!=NULL; p=Prenode(p))
visit(p);
}
先序线索二叉树 找后继√
- 若没有左孩子,后继结点是右孩子的第一个访问结点
先序线索二叉树 找前驱×
- 左右孩子都在后面,找不到前驱
- 除非有一个父节点
后序线索二叉树 找后继×
后序线索二叉树 找前驱√
树的初始化
顺序存储(双亲表示法)
双亲表示法:一组连续空间,伪指针指向双亲节点
回顾:二叉树的顺序存储是把结点编号和二叉树对应
typedef struct{
PTNode nodes[MAX_TREE_SIZE];
int n;
}PTree;
顺序+链式存储(孩子表示法)
孩子表示法:每个结点的孩子结点用单链表连接,顺序表中每个结点指向第一个孩子结点
struct CTNode{
int child;
struct CTNode *next;
};
typedef struct{
ElemType data;
struct CTNode *firstChild;
}CTBox;
typedef struct{
CTBox nodes[MAX_TREE_SIZE];
int n, r; //结点数和根的位置
}CTree;
链式存储(孩子兄弟表示法)
孩子兄弟表示法(也叫二叉树表示法,可用二叉树操作)
typedef struct CSNode{
ElemType data;
struct CSNode *firstchild, *nestsibling; //第一个孩子和右兄弟指针
}CSNode, *CSTree;
森林和二叉树转换
哈弗曼编码
并查集
双亲表示法
初始化
#define SIZE 13
INT UFSets[SIZE];
void Initial(int S[]){
for(int i=0; i<SIZE; i++)
S[i]=-1;
}
并、查 操作
//"Find"查操作,找x所属集合(根结点)
int Find(int S[], int x){
while(S[x]>=0)
x=S[x];
return x;
}
//"Union"并操作,两个集合(树)合并为一个
void Union(int S[], int Root1, int Root2){
if(Root1==Root2)
return;
S[Root2]=Root1;
}
并操作的优化:
- 用根结点的绝对值表示结点总数
- 优化:让小树合并到大树
void Union(int S[], int Root1, int Root2){
if(Root1==Root2)
return;
if(S[Root2]>S[Root1]){ //都是负值,越大说明结点越多
S[Root1]+=S[Root2];
S[Root2]=Root1;
} else {
S[Root2]+=S[Root1];
S[Root1]=Root2;
}
}
并查集的优化
int Find(int S[], int x){
int root=x;
while(S[root]>=0) //找到根结点root
root=S[root];
while(x!=root){ // x不指向根结点root,就压缩路径
int t=S[x]; //t指向x的父节点
S[x]=root; //x直接指向root
x=t; //再处理上一步结点
} //循环结束的时候,路径上的所有结点都挂在了root
return root;
}
图
初始化
领接矩阵法
数组实现的顺序存储,空间复杂度高,不适合存储稀疏图
#define MaxVertexNum 100
typedef struct{
char Vex[MaxVertexNum]; //顶点
int Edge[MaxVertexNum][MaxVertexNum]; //边
int vexnum, arcnum; //顶点数和边/弧数
}MGraph;
存储带权图(网)
#define MaxVertexNum 100
#define INFINITY 最大的int值 //用int的上限值表示“无穷,通常”无穷和0“表示结点之间没有边
typedef char VertexType;
typedef int EdgeType;
typedef struct{
VertexType Vex[MaxVertexNum];
EdgeType Edge[MaxVertexNum][MaxVertexNum];
int vexnum, arcnum;
}MGraph;
邻接表法
顺序+链式存储法,类似于树的孩子表示法
表示方式不唯一,适用于存储稀疏图
计算度和入度不方便,要遍历全部结点
//边/弧
typdef struct ArcNode{
int adjves; //边/弧指向的结点
struct ArcNode *next;
}ArcNode;
//顶点
typedef struct VNode{
VertexType data; //顶点信息
ArcNode *first; //第一条边
}VNode, AdjList[MaxVertexNum];
//图
typedef struct{
AdjList vertices; //直接用一个表示顶点的数组来表示图
int vexnum, arcnum;
}
十字链表
存储有向图
领接多重表
存储无向图
遍历操作
广度优先遍历
无向图调用BFS函数的次数=连通分量个数
有向图分情况,不同初始顶点次数不同
bool visited[MAX_VERTEX_NUM]; //访问标记数组,true表示已经访问,false表示未访问
void BFSTraverse(Graph G){
for(i=0; i<G.vexxnum; i++)
visited[i]=false;
InitQueue(Q);
for(i=0; i<G.vexnum; i++) //处理不是连通图的情况
if(!visited[i]==false)
BFS(G, i); //如果未访问过,就从i位置开始BFS
}
void BFS(Graph G, int v){
visit(v);
visited[v]=true;
EnQueue(Q, v);
while(!isEmpty(Q)){
DeQueue(Q, v);
for(w=FirstNeighbor(G, v); w>0; w=NextNeighbor(G, v, w))
if(!visited[w]){ //被访问过的就不再访问了
visit(w);
visited[w]==true;
EnQueue(Q, w);
}
}
}
/*
FirstNeighbor(G, v) 找图G中顶点v的第一个邻接点,存在返回顶点号,不存在返回-1
NextNeighbor(G, v, w) 已知w为v的邻接点,找图G中顶点v的除去w的下一个邻接点,存在返回顶点号,不存在返回-1
*/
深度优先遍历
对比:树的先根遍历
图有环时,可能出现多次遍历一个结点的情况
对于BFS很类似,但是DFS中的深度优先遍历是用递归实现的
bool visited[MAX_VERTEX_NUM];
void GFSTraverse(Graph G){
for(v=0; v<G.vexnum; v++)
visited[v]=false;
for(v=0; v=G.vexnum; v++)
if(!visited[v])
DFS(G, v);
}
void DFS(Graph G, int v){
visit(v);
visited[v]=true;
for(w=FirstNeighbor(G, v); w>0; w=NextNeighbor(G, v, w))
if(!visited[w]){
DFS(G, w);
}
}
无论是广度优先还是深度优先:领接矩阵表示方式唯一,遍历序列唯一;但是领接矩阵不唯一,遍历序列也不唯一