第二章 线性表
一、线性表的存储方式
1、顺序存储
/*****顺序存储类型*****/
typedef struct SqList
{
int data[Maxsize];//存放顺序表中元素
int length ;//存放顺序表长度
}SqList;
/*****初始化顺序表****/
void init(Sqlist &L)
{
L.length=0;
}
/*****求顺序表中指定元素的位置*****/
int get(Sqlist L,int i,int &e)
{
if(i<1||i>L.length)
return 0;
e=L.data[i-1];
return 1;
}
/*****按元素值查找****/
int locate(Sqlist L,int e)
{
int i=0;
while(i<L.length&&L.data[i]!=e) i++
if(i>L.length) return 0;
else return i+1;//逻辑序号 第几个元素
}
/*****插入数据元素******/
int insert(Sqlist &L,int e,int i)
{
int j;
if(i<0||i>L.length+1) return 0;
i--
for(i=L.length;j>i;i--)
L.data[j]=L.data[j-1];
L.data[i]=e;
reuturn 1;
}
/***删除数据元素****/
int delete(Sqlist &l;int &e)
{
int j;
if(i<1||i>length)
return 0;
i--;
e=L.data[i];
for(j=i;j<length;j++)//从删除元素位置开始计
L.data[j]=L.data[j+1];
L.length--;
return 1;
}
2、链式存储
typedef struct LNode
{
int data;
struct LNode*next;
}Linklist;
/*****头插法*****/
void create(Linklist *&L,int a[],int n)
{
linklist*p;
int i;
L=(Linklist*)malloc(sizeof(Linklist));//头结点
L=NULL;
for(i=0;i<n;i++)
{
s=(Linklist*)malloc(sizeof(linklist));
s->data=a[i];
s->next=L->next;
L-next=s;
}
}
/****尾插法****/
void create(Linklist *&L,int a[],int n)
{
Linklist *s,*r;int i;
L=(Linklist*)malloc(sizeof(Linklist));//头结点
L=NULL;
r=L;
for(i=0;i<n;i++)
{
s=(Linklist*)malloc(sizeof(linklist));
s->data=a[i];
r-next=s;
r=s;//r始终指向尾结点
}
r->next =NULL;
}
/*****按元素查找算法*****/
int locate(Listlink *l;int e)
{
Listlink*p=l->next;
int n=1;
while(p!=NULL&&p->data!=e)
{
p=p->next;
n++;
}
if(p==NULL)return 0;
else return n;//返回逻辑序号
}
/****删除节点操作******/
int del(Linklist*&L,int x)
{
Linklist*pre L;*p=pre->next;//pre指向p的前驱
while(p!=NULL &&p->data!=x)
{
pre=p;p=p->next;
}
if(p!=NULL)
{
pre->next=p->next;
free(p);
return 1;
}
else return 0;
}
二、循环链表
1、单向循环链表
/****头插法建立单向循环链表***/
void create(Linklist*&L,int a[],int n)
{
Linklist *s;int i;
L=(Linklist*)malloc(sizeof(Linklist));
L->next=L;//循环链表的最后一个结点的指针域指向头结点
for(i=0;i<n;i++)
{
s=(Linklist*)malloc(sizeof(Linklist));
s->data=a[i];
r->next=L->next;
L->next=s;
}
}
/***尾插法建立循环链表***/
void createlist(Linklist *&L,int e[],int n)
{
Linklist *s,*r;int i;//s为插入节点,r为链表尾结点
L=(Linklist*)malloc(sizeof(Linklist));
r=Linklist;
r=L;
for(i=0;i<n;i++)
{
s=(Linklist*)malloc(sizeof(Linklist));
s->data=a[i];
r->next=s;r=s;
}
r->next=L;//循环链表的最后一个结点的指针域指向头结点
}
/*****在循环链表中查找元素值为x的节点*****/
int find(Linklist*L,int x)
{
int n=1;
Linklist*p=L->next;
while(p!=L&&p->data!=x)
{
p=p->next;
n++;
}
if(p==L)return 0;//找到头了还没找到
else return n;
}
2、双向循环链表
/*****头插法建立循环双链表****/
void create(DLinklist *&L,int a[],int n)
{
DLinklist *s;int i;
L=(DLinklist*)malloc(sizeof(DLinklist));
L->next=L->prior=L;
for(i=0;i<n;i++)
{
s=(DLinklist*)malloc(sizeof(DLinklist));
s->data=a[i];
s->next=L->next;
if(L->next!=NULL)
{
L->next->prior=s;
}
L->next=s;
s->prior=L;
}
}
/***采用尾插法建立循环双链表***/
void create( DLinklist *&L,int a[],int n)
{
DLinklist *s,*r,int i;
L=(DLinklist*)malloc(sizeof (DLinklist));
r=L;
for(i=0;i<n;i++)
{
s=(DLinklist*)malloc(sizeof(DLinklist));
s->data=a[i];
r->next=s;
s->prior=r;
r=s;
}
r->next=L;
L->prior=r;
}
/***在循环链表中查找元素值为x的节点***/
int find(DLinklist*p,int x)
{
int n;
DLinklist*p=L->next;
while(p!=L&&p->data!=x)
{
p=p->next;
n++;
}
if(p==L)return 0;
else return n;
}
三、双向链表
/*****双链表结点类型*****/
typedef struct DLinklist
{
int data;
struct DLinklist *prior;//前驱结点
struct DLinklist *next;//后继结点
}DLinklist;
/*****采用头插法建立双向链表****/
void create(DLinklist *&L,int a[],int n)
{
DLinklist *s;int i;
L=(DLinklist*)malloc(sizeof(DLinklist));
L->next=L->prior=NULL;
for(i=0;i<n;i++)
{
s=(DLinklist*)malloc(sizeof(DLinklist));
s->data=data[i];
s->next=L->next;
if(L->!next=NULL)
L->next->prior=s;
L->next=s;
s->prior=L;
}
}
/****采用尾插法创建双向链表*****/
void create(DLinklist *&L,int a[],int n)
{
DLinklist *s,*r;int i;
L=(DLinklist*)malloc(sizeof(DLinklist));
r=L;//r始终指向终端结点,开始指向头结点
for(i=0;i<n;i++)
{
s=(DLinklist*)malloc(sizeof(DLinklist));
s->data=data[i];
r->next=s;
s->prior=r;
r=s;
}
r->next=NULL;//末尾指向NULL
}
/****查找指定元素*****/
int find(DLinklist*L,int x)
{
DLinklist*p=L->next;
int i=1;
while(p!=NULL&&p->data!=x)
{
i++;
p=p->next;
}
if(p==NULL)return NULL;//没找到
else return i;
}
/******在第i个位置插入节点*****/
int insert(DLinklist*L,int i,int e)
{
int j=0;
DLinklist *p=L,*s;
while(j<i-1&&p!=NULL)
{
j++;
p=p->next;//指示插入位置的前一个结点
}
if(p==NULL)
return 0;
else
{
s=(DLinklist*)malloc(sizeof(DLinklist));
s->data=e;
s->next=p->next;
if(p->next!=NULL)
{
p->next->prior=s;//NULL没有前驱结点
}
p->next=s;
s->prior=p;
}
}
/*****删除第i个结点******/
int delete(DLinklist*L,int i,int &e)
{
DLinklist *p=L,*q;
int j=0;
while(j<i-1&&p!=NULL)
{
p=p->next;//待删结点的前驱结点
p++;
}
if(p==NULL)
return 0;
else
{
q=p->next;
if(q==NULL)return 0;
p->next=q->next;
if(p->next!=NULL)
p->next->prior=p;
free(q);
return 1;
}
}
四、一元多项式的表示与相加
编写一个程序用单链表存储多项式,并实现两个一元多项式A与B相加的函数。A,B刚开始是无序的,A与B之和按降序排列
#include<stdio.h>
#include<stdlib.h>
/*创建结构体*/
typedef struct LinkList
{
float co;//系数
int ex;//指数
struct LinkList*next;
}LinkList;
/*创建链表*/
LinkList*create()
{
LinkList*head,*node,*end;//*node 循环创建一般结点;*end 在创建一个结点用来保存每次插入的结点(上一个结点)
head = (LinkList*)malloc(sizeof(LinkList));
end=head;
while (1)
{
node = (LinkList*)malloc(sizeof(LinkList));
scanf("%f %d",&node->co,&node->ex);
char c=getchar();
end->next=node;
end=node;
if(c=='\n'){
break;
}
}
node->next = NULL;
return head;
}
/* 升幂排序*/
//冒泡排序的主要思想两两相性比较,每比较一次会把一轮最大或最小的数放在最后
void up(LinkList *head){
LinkList *cur,*tail;
cur=head->next;
tail=NULL;
float temp1;
int temp;
if(cur==NULL||cur->next==NULL){
return;
}
while(cur!=tail){
while(cur->next!=tail){
if(cur->ex>cur->next->ex){
temp1=cur->co;
cur->co=cur->next->co;
cur->next->co=temp1;
temp=cur->ex;
cur->ex=cur->next->ex;
cur->next->ex=temp;
}
cur=cur->next;
}
tail=cur;
cur=head->next;
}
}
/*降幂排序*/
void down(LinkList *head){
LinkList *cur,*tail;
cur=head->next;
tail=NULL;
float temp1;
int temp;
if(cur==NULL||cur->next==NULL){
return;
}
while(cur!=tail){
while(cur->next!=tail){
if(cur->ex<cur->next->ex){
temp1=cur->co;
cur->co=cur->next->co;
cur->next->co=temp1;
temp=cur->ex;
cur->ex=cur->next->ex;
cur->next->ex=temp;
}
cur=cur->next;
}
tail=cur;
cur=head->next;
}
}
/*相加*/
void add(LinkList*head_A,LinkList*head_B)
{
LinkList *ptr_A,*ptr_B,*t,*del;
t=head_A;//pre此刻指向多项式A的头指针,后续记录上一个结点
float add;
ptr_A=head_A->next;
ptr_B=head_B->next;
while((ptr_A!=NULL&&ptr_B!=NULL))
{
if(ptr_A->ex<ptr_B->ex)
{
t=ptr_A;
ptr_A=ptr_A->next;
}
else if(ptr_A->ex>ptr_B->ex)
/*attention!*/
{
del=ptr_B->next;
ptr_B->next=ptr_A;
t->next=ptr_B;
t=ptr_B;
ptr_B=del;}
else if(ptr_A->ex==ptr_B->ex)
{
add=ptr_A->co+ptr_B->co;
if(add!=0)
{
ptr_A->co=add;
t=ptr_A;
}
else
{
t->next = ptr_A->next;//指向下一个结点
free(ptr_A); //释放p销毁结点
}
ptr_A = t->next;
del = ptr_B;
ptr_B= ptr_B->next;
free(del);
}
}
if (ptr_B)
{
t->next = ptr_B;//B链表剩余数本就连接
}
free(head_B);
}
int main()
{
LinkList*A,*B;
int n,i;//指定输出某一项的系数
A=create();B=create();
scanf("%d",&n);
up(A);up(B);
add(A,B);
down(A);
for(i=0;i<n;i++)
{
A=A->next;
}
printf("%.1f %d",A->co,A->ex);
return 0;
}
第三章 栈和队列
一、栈
top=base,栈空
1、顺序栈
(1)存储结构
(2)顺序栈算法
2、链栈
(1)存储结构
(2)链栈算法
3、应用
(1)数制转换
(2)括号匹配
(3)前缀中缀后缀表达式
(4)行编辑程序
(5)迷宫问题
(6)表达式求值
二、队列
1、顺序队列(算法实现为循环队列)
附设两个头指针,头指针指向队首元素,尾指针指向队尾元素的下一个位置。
(1)存储结构
(2)顺序队列(循环队列算法)
2、链队列
(1)存储结构
(2)链队列算法
第四章 串
一、基本概念
长度:串中字符的数目n
空串:零个字符的串
子串:
主串:
位置:
真子串:
相等:
空格串:
空串:
字典序:
二、定长顺序存储
1、存储结构
2、顺序串算法
(1)创建串
(2)串复制
(3)求串长
(4)串连接
(5)求子串
(6)串插入
(7)删除子串
(8)输出串
二、堆分配存储表示(链串)
1.存储结构
2、链串算法
(1)创建串
(2)串复制
(3)求串长
(4)串连接
(5)求子串
(6)串插入
(7)删除子串
(8)输出串
三、串的块链存储表示
除头指针外,还可以附设一个尾指针指示链表中的最后一个结点,并给出当前串的长度。设尾指针的目的是为了便于进行连接操作。注意:连接时需处理第一个串尾无效字符。
1、存储结构
#define Maxsize 100
typedef struct chunk{
char ch[Maxsize];
struct chunk *next;
}chunk;
typedef struct Lstring{
struct Lstring*head,*tail;
int curlen; //串的当前长度
}Lstring;
2、存储密度
存储密度=串值所占的存储位/实际分配的存储位
四、C/C++的标准string
五、串的模式匹配算法
第六章 树
一、基本概念
**
N个结点的树有N-1条边
**
树的结点:表示树中的元素,包括一个数据元素+若干指向其子树的分支
例如: A,B,C,D等
根结点(root):无前趋结点(或无父结点)的结点,例,A结点
结点的度(degree):结点拥有的子树数,如A的结点度为3
叶子(leaf)/终端节点:度为0的结点(无后继节点),如K,L,M
非终端结点/分支结点 :度不为0的结点。除根节点外,分支结点也称内部结点
树的度:树内各结点度的最大值
孩子(child):结点子树的根称为该结点的孩子
双亲(parents):孩子结点的上层结点
兄弟(sibling):同一双亲的孩子
结点的祖先:从根到该结点所经分支上的所有结点
结点的子孙:以某结点为根的子树中的任一结点都称为该结点的子孙
结点的层次(level):从根结点算起,根为第一层,它的孩子为第二层。若某结点在第L层,则它的子树的根在第L+1层。
堂兄弟:双亲结点在同一层的结点
深度(depth)或高度:树中结点的最大层次
如果将树中结点的各子树看成从左到右是有次序的(即不能互换),则称该树为有序树,否则称为无序树。
森林:m(m>=0)棵互不相交的树的集合.对树中的每个结点而言,其子树的集合即为森林。
路径,路径长度
从一个祖先结点到其子孙结点的一系列边称为树中一条路径
显然,从树根到树中任一个结点都有路径,且路径唯一
路径中边的条数称为路径长度,认为每个结点到自身有长0 的路径
二、树的存储结构
1、双亲表示法
typedef struct STree{
int data;//数据域
int parent;//双亲域
}STree[Maxsize];//定义一个类型别名STree,这个类型是一个结构体类型为元素类型的长度为Maxsize的数组,这个结构体类型的元素是int data和int parent
2、孩子表示法
typedef struct tnode{
int data;//数据域
struct tnode *child[M];//孩子指针域,M为树的度
}CTree;
3、孩子兄弟表示法
typedef struct cbnode{
int data;
struct cbnode *firstchild;
struct cbnode *nextsibling;
}CBTree;
三、二叉树
1、定义
二叉树是一种树形结构,其特点是每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点),并且,二叉树的子树有左右之分。
2、二叉树的重要性质
性质1:
性质2:
性质3:
N个结点的树有N-1条边,n0+n1+n2-1(总边数)=n00(由n0发散出去的边)+n11(由n1发散出去的边)+n2*2(由n2发散出去的边)
满二叉树
必须是二叉树的每一层上的结点数都达到最大,否则就不是满二叉树
完全二叉树
深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称之为完全二叉树。
特点:(1)叶子结点只可能在层次最大的两层上出现;(2)对任一结点,若其右分支下的子孙的最大层次为l,则其左分支下的子孙的最大层次必为l或l+1。
完全二叉树性质1:
不大于log2n的整数+1
完全二叉树性质2:
!!!
标号i从1开始
3、二叉树的存储结构
(1)顺序存储
typedef int SBTree[Maxsize];
(2)链式存储
typedef struct node{
int data;
struct node*lchild;
struct node*rchild;
}node;
4、二叉树的应用—优先队列
优先队列:特殊的队列,取出元素的顺序是按照元素的优先权大小,而不是元素进入队列的先后顺序。
用堆实现优先队列。
堆:(1)结构性:用数组表示的完全二叉树
(2)有序性:任一节点的关键字是其子树的所有结点的最大(或最小值)
※去掉堆顶中的最后元素仍是堆 ※一个堆去掉堆顶,其余元素形成2个堆,两个堆加一个元素作为根,得到一棵完全二叉树未必是堆。
实现在堆中正确加入和弹出数据元素的基本操作是筛选
筛选分为向上筛选和向下筛选两种
向上筛选:
A.新元素放在连续表末尾;B.执行一次向上筛选操作:在堆H 的最后加入一个元素e 后,不断用e 与其父结点的数据比较,如果e 较小就交换两个元素的位置。直至e 的父结点的数据小于等于e 。
向下筛选:
A. 弹出当时堆顶,从堆最后取一个元素放在堆顶;B.执行一次向下筛选操作:设两个堆B,C 加根元素e 构成一棵完全二叉树,e 与B、C 的顶元素比,最小者作为整个堆的顶。若e最小,结束;若e 不是最小,最小的必为B(或C)顶元素,则把e 放入去掉堆顶的B(或C)(同样问题但规模更小)。两种情况结束:某次比较中e 最小,以它为顶的局部树为堆;e 落到底,自身就是一个堆,整个结构也成为堆。
#define Maxsize 100
#define Maxdata 100
/**********堆类型定义****************/
typedef struct HNode{
int *data;//存储元素的数组
int size;//堆中当前元素的个数
int capacity;//堆的最大容量
}HNode;
HNode *maxheap;//大顶堆
HNode *minheap;//小顶堆
/********创建容量为Maxsize的空的大顶堆**********/
HNode* CreatHeap(int Maxsize)
{
HNode H=(HNode*)malloc(sizeof(HNode));
H->data=(int*)malloc((Maxsize+1)sizeof(int));
//从下标为1开始存放,需要Maxsize+1个空间
H->size=0;
H->capacity=Maxsize;
H->data[0]=Maxdata;//哨兵为大于堆中所有可能元素的值
return H;
}
/******判断堆是否已满*********/
int IsFull( HNode* H )
{
return (H->Size == H->Capacity);
}
/*******将元素X插入大顶堆,其中H->data[0]为哨兵**********/
int insert(HNode*H,int x)
{
if(IsFull)
return 0;
i=++H->size;//i指向插入后堆中最后一个元素的位置,这条语句不仅能把最后一个元素的位置赋给i,也可以使得size+1(先+1再赋给i)
for(;H->data[i/2]<x;i/=2)
H->data[i]=H->data[i/2];//上筛X
H->data[i]=x;//将x插入
return 1;
}
/******判断是否为空堆*********/
int IsEmpty(HNode*H)
{
return (H—>size==0)
}
/*******从堆中取出键值最大的元素*********/
int DelMax(HNode*H)
{
int Parent ,Child;
int MaxItem,x;
if(IsEmpty)
{
return -1;
}
MaxItem=H->data[1];//取出根节点存放的最大值
/*用大顶堆中最后一个元素从根节点开始向上筛选*/
x=H->data[H->size--]//先把最后的元素赋给x再-1
for(parent=1;parent*2<=H->size;parent=child)
{
child=parent*2;
if((child!=H->size)&&(H->data[child]<H->data[child+1]))
child++;//child指向左右子结点的较大者
if(x>=H->data[child]) break;
else/*下筛x*/
H->data[parent]=H->data[child];
}
}
/*****建造大顶堆****/
void percdown(HNode*H,int p)
{
/*向下筛选,将H中以H->data[p]为根的子堆调整为最大堆 */
int parent,child;
int x;
x=H->data[p];
for(parent=p;parent*2<=H->size;parent=child)
{
child=parent*2;
if((child!=H->size)&&(H->data[child]<H->data[child+1]))
child++;
if(x>=H->data[child]) break;
else
H->data[parent]=H->data[child];
}
H->data[parent]=x;
}
void BuildHeap(HNode*H)
/**调整H->data[]中的元素,使满足大顶堆的有序性**/
/*这里假设H->size个元素已经存在H->data[]中*/
{
int i;
/*从最后一个结点的父结点开始,到根节点1*/
for(i=H->size/2;i>0;i--)
{
percdown(H,i);
}
}
5、树与二叉树的转换
树-二叉树:
(1)在所有相邻兄弟节点(森林中每棵树的根节点可看成兄弟节点)之间加一水平连线。
(2)对每个非叶子结点K,除了其最左边的孩子结点外,删去k与其他孩子结点的连线。
(3)将图形规整化,所有水平线段以左边结点为轴心顺时针旋转45度。
树转换成的二叉树的根节点的右孩子始终为空。
二叉树-树:
(1)加线:若p结点是双亲结点的左孩子,则将p的右孩子,右孩子的右孩子……沿分支找到的所有右孩子,都与p的双亲用线连起来
(2)抹线:抹掉原二叉树中双亲与右孩子之间的连线调整
(3)将结点按层次排列,形成树结构
6、二叉树的遍历算法
/******先序遍历*****/
//递归法
void PreOrder(BTNode*b)
{
if(b!=NULL)
{
printf("%d",b->data);
PreOrder(b->lchild);
PreOrder(b->rchild);
}
}
//非递归法
/*沿左子树一直走到末端,此过程中,访问所遇结点,并依次把所遇结点进栈;从栈顶退出某结点,并指向该结点的右孩子如此重复,直到栈为空并指针为空止。
*/
void PreOrder(BTNode*b)
{
BTNode *ST[Maxsize],*p;
int top=-1;//初始指向栈底
if(b!=NULL)//树不为空时执行
{
top++;
St[top]=b;//存放BTNode*类型的栈
while(top>-1)
{
p=St[top];
top--
printf("%d",p->data);
if(p->lchild!=NULL)
{
top++;
ST[top]=p->lchild;
}
if(p->rchild!=NULL)
{
top++;
ST[top]=p->rchild;
}
}
printf("\n");
}
}
/******中序遍历*****/
//递归法
void InOrder(BTNode*b)
{
if(b!=NULL)//树不为空时执行
{
PreOrder(b->lchild);
printf("%d",b->data);
PreOrder(b->rchild);
}
}
//非递归法
void InOrder(BTNode*p)
{
BTNode* ST[Maxsize],BT*
int top=-1;
if(b!=NULL)//树不为空时执行
{
p=b;
while(top>-1||p!=NULL)//栈不为空或者当前结点不为空执行循环
{
while(p!=NULL)
{
top++;
ST[top]=p;
p=p->lchild;
}
if(top>-1)
{
p=ST[top];
top--;
printf("%d",p->data);
p=p->rchild;
}
}
printf("\n");
}
}
/******后序遍历*****/
//递归法
void PostOrder(BTNode*b)
{
if(b!=NULL)
{
PreOrder(b->lchild);
PreOrder(b->rchild);
printf("%d",b->data);
}
}
//非递归法
void PostOrder(BTNode*b)
{
BTNode *ST[Maxsize];
BTNode *p;
int flag,top=-1;
if(b!=NULL)
{
do{
while(b!=NULL)
{
top++;
ST[top]=b;
b=b->lchild;
}
p=NULL;
flag=1;
while(top!=1&&flag)
{
b=ST[top];
if(b->rchild==p)
{
printf("%d",b->data);
top--;
p=b;
}
else
{
b=b->rchild;
flag=0;
}
}
}while(top!=-1);
printf("\n");
}
}
/*****层次遍历,借用队列*******/
void Levelorder(BTNode *b)
{
BTNode *p;
BTNode *qu[Maxsize];
int front, rear;
front=rear=0;
rear++;
qu[rear]=b;
while (front!=rear)
{
front=(front+1)%Maxsize;
p=qu[front];
printf("%c",p->data);
if(p->lchild!=NULL)
{
rear=(rear+1)%Maxsize;
qu[rear]=p->lchild;
}
if(p->rchild!=NULL)
{
rear=(rear+1)%Maxsize;
qu[rear]=p->rchild;
}
}
}
(1)给定先序+中序或后序+中序才能唯一确定一颗二叉树
(2)给定先序序列可以唯一确定一颗二叉排序树
7、二叉树的基本运算算法
/******创建二叉树******/
//采用括号法表示二叉树字符串(A,B)
void create(BTNode *&b,char *str)
{
BTNode *ST[Maxsize],*p=NULL;
int top=-1,k,j=0;char ch;
b=NULL;
ch=str[j];
while("ch!='\0'")
{
switch (ch)
{
case'(':top++;ST[top]=p;k=1;break;//前面刚创建的结点作为双亲结点入栈,括号后创建左孩子
case')':top--;break;//括号完毕表示左右孩子创建完毕
case',':k=2;break;//逗号后表示创建右孩子
default:p=(BTNode*)malloc(sizeof(BTNode))//创建结点,分配空间
p->data=ch;
p->lchild=p->rchild=NULL;
if(b=NULL);
b=p;
else
{
switch(k)
{
case 1:St[top]->lchild=p;break;
case 2:St[top]->rchild=p;break;
}
}
}
ch=str[++j];
}
}
/*****查找结点******/
BTNode *FindNode(BTNode *b,int x)
{
BTNode *p;
if(b=NULL)
return NULL
else if(b->data==x)
return b;
else
{
p=FindNode(b->lchild,x);
if(p!=NULL)
return p;
else return FindNode (b->rchild,x);
}
}
/*****查找孩子结点*****/
BTNode *LchildeNode(BTNode*p)
{
return p->lchild;
}
BTNode *RchildeNode(BTNode*p)
{
return p->rchild;
}
/****求高度***/
int BTNodeDepth(BTNode*b)
{
int lchilddep,rchilddep;
if(b=NULL)return 0;//高度从0开始计数
else {
lchilddep=BTNodeDepth(b->lchild);
rchilddep=BTNodeDepth(b->rchild);
return (lchildedep>rchilddep)?(lchilddep+1):(rchilddep+1);
}
}
/***输出二叉树***/
void print(BTNode *b)
{
if(b!=NULL)
{
printf("%c",b->data);
if(b->lchilde!=NULL||b->rchild!=NULL)
{
printf("(");
print(b->lchild);
if(b->rchild!=NULL)
print(b->rchild);
printf(")") ;
}
}
}
8、二叉树的构造
/*******已知先序中序遍历*******/
BTNode *create(char*pre,char*in,int n)//pre存放先序序列,in存放中序序列,n为in中的字符个数,算法返回二叉链根节点指针b
{
BTNode *s;
char b;
int k;
if(n<0)return NULL
b=(BTNode*)malloc (sizeof(BTNode));
b->data=pre;
for(p=in;p<in+n;p++)
if(*p==*pre)
break;
k=p-in;
b->lchild=creat(pre+1,in,k);
b->rchild=creat(pre+k+1,p+1,n-k-1);
return b;
}
/*****已知中序后序遍历*****/
BTNode *create(char*post,char*in,int n)//post存放后序序列,in存放中序序列,n为in中的字符个数,算法返回二叉链根节点指针b
{
BTNode *b;
char r,*p;
int k;
if(n<0)return NULL;
r=*(post+n-1);
b=(BTNode*)malloc(sizeof(BTNode));
b->data=r;
for(p=in;p<in+n;p++)
if(*p==r) break;
k=p-in;
b->lchild=create(post,in,k);
b->rchild=create(post+k;p+1;n-k-1);
return b;
}
9、线索二叉树
(1)基本概念
前驱与后继
在二叉树的先序、中序或后序遍历序列中两个相邻的结点互称为前驱与后继
线索
指向前驱或后继结点的指针
线索二叉树
加上线索的二叉树
线索链表
包含 “线索” 的存储结构,称作 “线索链表”
(2)算法
/****二叉树节点类型声明****/
typedef struct node
{
int data;//数据域
int ltag,rtag;//增加的线索标记
struct node *lchild;//左孩子或线索指针
struct node *rchild;//右孩子或线索指针
}TBTNode;
/****中序线索化二叉树****/
TBTNODE *pre;//全局变量
//函数在TBTNODE *creaThread(TBTNODE *b)中被调用
void Thread(TBTNODE *&p)//线索化二叉树
{
if(p!=NULL)
{
Thread(p->lchild);
if(p->lchild==NULL)
{
p->lchild=pre;
p->ltag=1;
}
else p->ltag=0;
if(pre->rchild==NULL)
{
pre->rchild=p;
pre->rtag=1;
}
else pre->rtag=0;
pre=p;
Thread(p->rchild);
}
}
TBTNODE *creaThread(TBTNODE *b)
{
TBTNODE *root;
root=(TBTNODE*)malloc(sizeof(TBTNODE));
root->ltag=0;root->rtag=1;
root->rchild=b;
if (b==NULL)
root->lchild=root;
else
{
root->lchild=b;
pre=root;
Thread(b);
pre->rchild=root;
pre->rtag=1;
root->rchild=pre;
}
return root;
}
/***遍历线索化二叉树***/
void ThInorder(TBTNode *tb)
{
TBTNode *p=tb->lchild;
while(p!=tb)
{
while (p->ltag==0)
p=p->lchild;
printf("%c",p->data);
while(p->rtag==1&&p->rchild!=tb)//有孩子不等于tb表示访问未结束
{
p=p->rchild;//最后一个结点右孩子为root
printf("%c",p->data);
}
p=p->rchild;
}
}
10、二叉搜索树
BinTree Insert( BinTree BST, ElementType X )
{
if( !BST ){ /* 若原树为空,生成并返回一个结点的二叉搜索树 */
BST = (BinTree)malloc(sizeof(struct TNode));
BST->Data = X;
BST->Left = BST->Right = NULL;
}
else { /* 开始找要插入元素的位置 */
if( X < BST->Data )
BST->Left = Insert( BST->Left, X ); /*递归插入左子树*/
else if( X > BST->Data )
BST->Right = Insert( BST->Right, X ); /*递归插入右子树*/
/* else X已经存在,什么都不做 */
}
return BST;
}
BinTree Delete( BinTree BST, ElementType X )
{
Position Tmp;
if( !BST )
printf("要删除的元素未找到");
else {
if( X < BST->Data )
BST->Left = Delete( BST->Left, X ); /* 从左子树递归删除 */
else if( X > BST->Data )
BST->Right = Delete( BST->Right, X ); /* 从右子树递归删除 */
else { /* BST就是要删除的结点 */
/* 如果被删除结点有左右两个子结点 */
if( BST->Left && BST->Right ) {
/* 从右子树中找最小的元素填充删除结点 */
Tmp = FindMin( BST->Right );
BST->Data = Tmp->Data;
/* 从右子树中删除最小元素 */
BST->Right = Delete( BST->Right, BST->Data );
}
else { /* 被删除结点有一个或无子结点 */
Tmp = BST;
if( !BST->Left ) /* 只有右孩子或无子结点 */
BST = BST->Right;
else /* 只有左孩子 */
BST = BST->Left;
free( Tmp );
}
}
}
return BST;
}
11、哈夫曼树
(1)定义
(2)特点
A.没有度为1的结点 (这类输又称严格的(或正则的)二叉树)B.n个叶子结点的哈夫曼树共有2n-1个结点 C.哈夫曼树的任意非叶结点的左右子树交换后仍是哈夫曼树
(3)构造哈夫曼树
/*C语言*/
typedef struct HTNode
{
int data;
float weight;
int parent;
int lchild;
int rchild;
}HTNode;
void create(HTNode ht[],int n0)
{
int i,j,k,lnode,rnode;
float min1,min2;
for(i=0;i<2*n0-1;i++)
ht[i].parent=ht[i].lchild=ht[i].rchild=-1;
for(i=n-;i<2*n0-1;i++)
{
min1=rnode=-1;
for(k=0;k<i-1;k++)
if(ht[k].parent==-1)
{
if(ht[k].weight<min1)
{
min2=min1;rnode=lnode;
min1=ht[k].weight;lnode=k;
}
else if(ht[k].weight<min2)
{min2=ht[k].weight;rnode=k;}
}
ht[lnode].parent=i;ht[rnode].parent=i;
ht[i].weight=ht[lnode].weight+ht[rnode].weight;
ht[i].lchild=lnode;ht[i].rchild=rnode;
}
}
/*C++*/
#include<iostream>
#include<stdio.h>
#include<stack>
#include<windows.h>
#define Maxsize 50
using namespace std;
/**************定义结构体***********************/
typedef struct HaTree
{
int weight;
struct HaTree* r_next;
struct HaTree* l_next;
}HaTree;
/**********输入数组冒泡排序****************/
void sort(int arr[], int num)
{
int i, j;
int temp;
//控制比较轮数
for (i = 1; i < num; i++)
{
//每轮比多少
for (j = 0; j < num - i; j++)
if (arr[j] > arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
/*************打印哈夫曼树*******************/
void print(HaTree* T)
{
if (T)
{
print(T->l_next);
cout << T->weight << " ";
print(T->r_next);
}
else return;
}
/**************获取对应结点*****************/
HaTree* GetNode(int num)
{
HaTree* tree = new HaTree;
tree->weight = num;
tree->l_next = NULL;
tree->r_next = NULL;
return tree;
}
/*****************创建哈夫曼树*******************/
/*
主要算法思想为:首先对输入数组中的数采用排序算法,按从大到小的顺序排序,然后依次入栈(栈顶为
小元素),每次取出栈顶两个元素(即当前剩余序列中最小的两个元素),算出他们的根节点。
定义了两个栈,其中一个栈用来存放有序元素(栈顶为小元素),另一个栈起到辅助排序的作用。
*/
HaTree* CreateHaTree(int arr[], int length)
{
stack<HaTree*> Arrtask;//存放有序元素(栈顶为小元素)
stack<HaTree*> stak;//辅助排序
int i;
/********将递增顺序的数组入栈********/
for (i = length-1; i>=0; i--)
{
HaTree* T = GetNode(arr[i]);
Arrtask.push(T);
}
while (Arrtask.size() != 1)
{
HaTree* n = Arrtask.top();
Arrtask.pop();
HaTree* m = Arrtask.top();
Arrtask.pop();//取出栈顶两个元素
HaTree* p = GetNode(n->weight + m->weight);//计算出根节点权值
/*********左子树为小于根节点的数,右子树为大于根节点的数**********/
if (n->weight > m->weight)
{
p->l_next = m;
p->r_next = n;
}
else
{
p->l_next = n;
p->r_next = m;
}
/*在元素栈不为空(即哈夫曼树构造未完成)时,比较当前获得的根节点与栈顶元素的权值
*/
while (!Arrtask.empty() && p->weight > Arrtask.top()->weight)
{
stak.push(Arrtask.top());//把小于根节点的元素值放入stack中,此时stack中栈顶为大元素
Arrtask.pop();/*当前根节点权重值大于栈顶元素,需要重新排序(因为我们每次
取权值最小的两个节点)*/
}
Arrtask.push(p);//将当前根节点压栈
while (!stak.empty())
{
Arrtask.push(stak.top());//stack 中权值有序进入Arrtask栈,该栈栈顶为小元素
stak.pop();//清空辅助栈
}
}
return Arrtask.top();
}
int main()
{
HaTree* T = NULL;
int num,i,k;
int arr[Maxsize];
num=Maxsize;
cout << "请输入带权路径长度\n";
i=0;
do{
cin >> arr[i];
i++;
}while(i<num&&getchar()!='\n');
num=i;
sort(arr, num);
T = CreateHaTree(arr, num);
cout << "得到的哈夫曼树\n";
print(T);
cout << endl;
cout << "得到的哈夫曼树的根结点\n";
cout << T->weight ;
//system("pause");
return 0;
}
(2)编码:从叶子结点出发走一条到根的路径
typedef struct{
char cd[N];//存放当前节点的哈夫曼码
int start;//起始位置
}HCode;
void CreateHCode(HTNode ht[],HCode hcd[],int n0)
{
int i,f,c;
HCode hc;
for(i=0;i<n0;i++)
{
hc.start=n;c=i;
f=ht[i].parent;
while(!f=-1)
{
if(ht[f].lchild==c)
hc.cd[hc.start--]='0';
else
hc.cd[hc.start--]='1';
c=f;f=ht[f].parent;
}
hc.start++;
hcd[i]=hc;
}
}
(3)译码
第七章 图
一、定义
邻接点
无向图中,若边(Vx,Vy)属于 E,则Vx、Vy互为邻接点
有向图中,若弧<Vx,Vy>属于E,则Vy是Vx的邻接点,反之,不成立。Vx邻接到Vy,Vy邻接自Vx。
极大连通子图:
(1)极大顶点数:再加一个顶点就不连通(2)包含图中所有顶点相连的所有边
强连通图每个顶点至少一个入度和出度
弱连通图:有向图变为无向图后是连通图的有向图
二、图的存储结构
(1)邻接矩阵
/***图的邻接矩阵存储类型****/
#define MAXV 100 //最大顶点个数
typedef struct VertexType
{
int no;//顶点编号
char info;//顶点其他信息
}VertexType ; // 顶点类型
typedef struct MGraph//图的定义
{
int edges[MAXV][MAXV];//邻接矩阵
int n,e;//分别为顶点数和边数
VertexType vexs[MAXV];//存放顶点信息
}MGraph; //声明图的邻接矩阵类型
/****生成图的邻接矩阵****/
/****输出图的邻接矩阵****/
(2)邻接表
/***图的邻接表存储类型****/
/****生成图的邻接表****/
/****输出图的邻接表****/
第九章 查找
一、基本概念
查找表:一组同一类型的数据元素(或记录)构成的集合
关键字:数据元素中某个数据项的值,用它可以标识一个数据元素。若此关键字可以唯一的标识一个记录,则称此关键字为主关键字。(对不同的记录,主关键字均不同)反之,称用以识别若干记录的关键字为次关键字。当数据元素只有一个数据项时,其关键字即为该数据元素的值。
查找:是根据给定的某个值,在查找表中确定一个关键字等于给定值的记录或数据元素。若查找表中存在这样一个记录,则称“查找成功”,查找结果给出整个记录的信息,或指示该记录在查找表中的位置;否则称“查找不成功”,查找结果给出“空记录”或“空指针”。
平均查找长度: 为确定记录在表中的位置,需和给定值进行比较的关键字的个数的期望值。
对含有n个数据元素的查找表,查找成功时的平均查找长度为
二、静态查找表
1、顺序表的查找
顺序查找:从表中最后一个记录开始,逐个进行记录的关键字和给定值的比较,若某个记录的关键字和给定值比较相等则查找成功,找到所查记录;反之,若直至第一个记录,其关键字和给定值的比较都不等,则表明表中没有所查记录,查找不成功。
/**************存储结构1*********************/
#define Maxsize 100
typedef struct seqlist{
int key;//关键字数据类型
int data;//其他数据项
}seqlist;//记录类型
seqlist r[Maxsize];//声明顺序表类型
/***********顺序查找--从前往后***************/
//法1:
int search(seqlist r,int k)//查找关键字为k的记录
{
int i=0;
while(i<n;r[i].key!=k)
i++;//从表头往后找
if(i>=n)
return -1;
else return i;
}
//法2:
/*********存储结构2*********/
typedef struct seqlist{
int data[Maxsize];
int length;
}seqlist;
seqlist r;
/********顺序查找--从后往前*********/
int search(seqlist r,int k)
{
int i;
i=r.length;
r.data[0]=key;
while(r.data[i]!=key)
i--;
return i;
}
算法分析:
2、有序表的查找
折半查找:先确定待查记录所在的范围(区间),然后逐步缩小范围直到找到或找不到该记录为止。
先决条件:查找表中的数据元素必须有序
由于折半查找算法中需要通过上、下界确定查找区间,所以它仅适合于顺序表,不适合链表
/**************存储结构1*********************/
#define Maxsize 100
typedef struct seqlist{
int key;//关键字数据类型
int data;//其他数据项
}seqlist;//记录类型
seqlist r[Maxsize];//声明顺序表类型
/***********折半查找****************/
int binsearch(seqlist r,int length,int key)
{
int low=0;
int high=length-1;
while(low<=high)
{
mid=(low+high)/2
if(r[mid].key==key)
{
return mid;
}
if(r[mid].key<key)
high=mid-1;
else
low=mid+1;
}
return -1;
}
/*********存储结构2*********/
typedef struct seqlist{
int data[Maxsize];
int length;
}seqlist;
/*********折半查找**********/
int search(seqlist q,int key)
{
int low=0;
int high=seqlist.length;
mid=(low+high)/2
while(low<=high)
{
if(seplist.data[mid]==key)
return mid;
else if(seqlist.data[mid]<key)
low=mid+1;
else
high=mid-1;
}
return -1;
}
折半查找的查找过程可以用一棵二叉树来描述。其中,树中的每个结点表示一个记录,结点中的值为该记录在表中的位置,通常称这个描述查找过程的二叉树为判定树。
判定树的构造过程:把整个查找区间的中间位置作为根结点,左子表或右子表的中间位置作为根的左孩子或右孩子,依此类推,得到的二叉树即为描述二分查找的判定树。
折半查找在查找成功时进行比较的关键字个数最多不超过树的深度。具有n个结点的判定树的深度为:
所以,折半查找法在查找成功时和给定值进行比较的关键字个数至多为:
折半查找查找不成功时和给定值进行比较的关键字个数最多也不超过
时间复杂度 :
3、比较
顺序查找与折半查找比较
(1)顺序查找的查找表既可以采用顺序存储结构,又可以采用链式存储结构;折半查找的查找表只能采用顺序存储结构。
(2)顺序查找大查找表可以是有序的也可以是无序的;折半查找的查找表必须是有序的。
4、其他查找方式
(1)斐波那契查找
(2)插值查找
三、动态查找表
1、二叉排序树
2、平衡二叉树
3、B-树
第十章 内部排序
一、排序的分类
二、插入排序
1、直接插入排序