typedef struct PolyNode *Polynomial;
struct PolyNode
{
int cofe;
int expon;
Polynomial link;
};
线性表是由同类型数据元素构成有序序列的线性集合
线性表的抽象数据类型描述
线性表利用数组连续存储空间的顺序存放线性表的各元素
typedef struct LNode*List;
struct LNode
{
ElementType Data[Maxsize];
int Last;
};
struct LNode L;
List Ptrl;
访问下表为i的元素方法为:L.Data[i]或者PtrL->Data[i]
线性表的长度为L.last+1或者PtrL->Last+1
List MakeEmpty()
{
List PtrL;
PtrL=(List)malloc(sizeof(struct LNode));
PtrL->Last = -1;
return PtrL;
}
int Find(ElementType X, List PtrL)
{
int i = 0;
while (i <= PtrL->Last && PtrL.Data[i] != X)
i++;
if(i>PtrL->Last)return -1;
else return i;
}
//时间复杂度为O(n)
void Insert(ElementType X, int i, List PtrL)
{
int j;
if(PtrL->Last==MAXSIZE-1)
{
printf("该表已满");
return;
}
if(i>=PtrL->Last+2||i<1)
{
printf("位置不合法");
return;
}
for(j = PtrL->Last; j >= i-1; j --)
PtrL->Data[j+1] = PtrL->Data[j];
//将第i个元素插入
//因为下标从0开始所以为i-1
PtrL->Data[i-1] = X;
//因为整体向后移动,要使Last+1继续指向最后一个元素
PtrL->Last++;
}
void Delete(int i ,List PtrL)
{
int j;
//检查i位置是否合法
if(i>=PtrL+2||i<1)
{
printf("不存在第%d个元素",i);
return;
}
for( j = i; j <= PtrL->Last; j ++)
PtrL->Data[j-1]=PtrL->Data[j];
//更新Last
PtrL->Last--;
return;
}
typedef struct LNode *List;
struct LNode
{
ElementType Data;
List Next;
};
struct LNode L;
List PtrL;
//在这里注意与顺序存储的区别
int Length(List PtrL)
{
//令p指向表的第一个节点
List p = PtrL;
int j;
while(j)
{
p = p->Next;
j++;
}
return j;
//返回的j即为表的长度
}
List Findkth( int k, List PtrL)
{
List p = PtrL;
int i = 1;
while(p!=NULL&&i<k)
{
p=p->Next;
i++;
}
if(i==k)return i;
else return NULL;
}
List Find( ElementType X, List PtrL)
{
List p = PtrL;
while(p!=NULL&&p->Next!=X)
p = p->Next;
return p;
}
List Insert(ElementType X, int i, List PtrL)
{
list p,s;
if(i==1)
{
s=(List)malloc(sizeof(struct LNode));
s->Data = X;
s->Next = PtrL;
return s;
}
//此处查找的节点为i-1
p = Findkth(i-1,PtrL);
//该结点不存在
if(p==NULL)
{
printf("参数出错");
return NULL;
}
//申请新的结点
else
{
s=(List)malloc(sizeof(struct LNode));
s->Data = X;
s->Next = p->Next;
p->Next = s;
return PtrL;
}
//将第i个结点插在i-1个结点的后面
List Delete(int i,List PtrL)
{
List p,s;
//删除结点分为删除头结点和第i个结点
if(i==1)
{
s = PtrL;
if(PtrL!=NULL)
PtrL = PtrL->Next;
else return NULL;
free(s);
return PtrL;
}
p = Findkth(i-1,PtrL);
//查找i-1个结点和i个结点
if(p==NULL)
{
printf("第%d个结点不存在",i-1);
return NULL:
}
else if (p->Next==NULL)
{
printf("第%d个结点不存在",i);
return NULL;
}
else
{
s=p->Next;//s指向i
p->Next=s->Next;//删除操作
free(s);
return PtrL;
}
广义表和多重链表
一元多项式可以用上述式子表示,二元多项式又该如何表示?
可以用“复杂”链表表示
typedef struct GNode*GList;
struct GNode
{
int Tag;//标志域,0表示结点是单元素,1表示结点是广义表
union
{
ElementType Data;
GList Sublist;
}URegion;
//这里数据域Data(单元素)和指针域Sublist复用,共用存储空间
GList Next;//指向后继结点
};
多重链表
多重链表中的结点属于多个链
多重链表中的结点指针域有很多,
但是包含两个指针域的链表并不一定是多重链表,
比如双向链表不是多重链表
多重链表可以用在树和图中实现存储
#define Maxsize<存储数据元素的最大值>
typedef struct SNode *Stack
struct SNode
{
ElementType Data[Maxsize];
int Top;
};
void Push(Stack PtrL,ElementType item)
{
if(PtrL->Top==Maxsize-1)
{
printf("堆栈满");
return;
}
else
{
PtrL->Data[++(PtrL->Top)] = item;
return;
}
}
ElementType Pop(Stack PtrL)
{
if(PtrL->Top==-1)
{
printf("堆栈空");
return ERROR;
}
else
return (PtrL->Data[(PtrL->Top)--]);
}
用数组实现两个堆栈,最大利用数组空间,若有空间则可以实现入栈
两个栈分别从数组的两头开始向中间生长,当两个栈的栈顶指针相遇时,表示两个栈都已经满
#define MaxSize <存储数据元素的最大个数>
struct DoubleStack{
ElementType Data[MaxSize];
int Top1;
int Top2;
//其中top1和top2两个指针
}S;
S.Top1=-1;
S.Top2=MaxSize;
}
void Push(struct DoubleStack *PtrS,ElementType item,int Tag)
{
if(PtrS->Top2-PtrS->Top1 == 1)
{
printf("堆栈满");
return;
}
if(Tag==1)
PtrS->Data[++(PtrS->Top1)] = item;
else
PtrS->Data[--(PtrS->Top2)] =item;
}
ElementType Pop(struct DoubleStack *PtrS, int Tag)
{
if(Tag==1)
{
if(PtrS->Top1 == -1)
{
printf("堆栈1空");
return NULL;
}
else return PtrS->Data[(PtrS->Top1)--];
}
else
{
if(PtrS->Top2 == Maxsize)
{
printf("堆栈空");
return NULL;
}
else return PtrS->Data[(PtrS->Top2)++];
}
}
typedef struct SNode *Stack;
struct SNode
{
ElementType Data;
struct SNode *Next;
};
Stack CreatStack()
{
Stack s;
s = (Stack)malloc (sizeof(struct SNode));
s->Next = NULL;
return s;
}
int Empty(Stack s)
{
return (s->Next == NULL);
}
void Push(ElementType item,Stack s)
{
struct SNode *Temcell;
Tmpcell = (struct *SNode)malloc(sizeof (struct SNode));
Tmpcell->Element = item;//赋值
Tmpcell->Next = s->Next;
s->Next = Tmpcell;
}
ElementType Pop(Stack s)
{
struct SNode *Firstcell;
ElementType TopElement;
if(Empty(s))
{
printf("堆栈空");
return NULL;
}
else
{
//Firstcell是删除的元素,TopElement是栈顶元素
Firstcell = s-Next;
s->Next = Firstcell->Next;
TopElement = Firstcell->Element;
free(Firstcell);
return TopElement;
}
}
插入和删除操作:只能在一端插入,而在另一端删除
数据插入:入队列
数据删除:出队列
先进先出(FIFO)First In First Out
队列存储的实现
队列的顺序存储结构通常由一个一维数组和一个记录队列头元素位置的变量ront以及一个记录队列尾元素位置的变量rear组成
#define Maxsize<数据元素的最大个数>
struct QNode{
ElementType Data[Maxsize];
int rear;
int front;
};
typedef struct QNode *Queue;
//front 指的是第一个元素之前的位置
在顺环队列判断是否满的问题上使用额外标记Tag域或者Size
void AddQ(Queue PtrL,ElementType item)
{
if(PtrQ->rear+1)%Maxsize ==PtrQ->front)
{
printf("队列满");
return;
}
PtrQ->rear = (PtrQ->rear+1)%Maxsize;
PtrQ->Data[PtrQ->rear]=item;
}
ElementType DQ(Queue PtrQ)
{
if(PtrQ->rear==PtrQ->front)
{
printf("队列空");
return ERROR;
}
else
{
PtrQ->front = (PtrQ->front+1)%Maxsize;
return PtrQ->Data[PtrQ->front];
}
}
struct Node{
ElementType Data;
struct Node *Next;
};
struct QNode{
struct Node *rear;
struct Node *front;
};
typedef struct QNode *Queue;
Queue PtrQ;
ElementType DQ(Queue PtrQ)
{
struct Node *Frontcell;
ElementType Frontelement;
if(PtrQ->front==NULL)
{
printf("空");
return ERROR;
}
Frontcell = PtrQ->front;
//分情况讨论,队列只有一个元素和多个元素
if(PtrQ->front == PtrQ->rear)
PtrQ->front=PtrQ->rear=NULL;
else
PtrQ->front = PtrQ->front->Next;
Frontelement = Frontcell->Data;
free(Frontcell);
return Frontelement;
}
struct PolyNode{
int coef;//系数
int expon;//指数
struct PolyNode *link;
};
typedef struct PolyNode *Polynomial;
Polynomial P1,P2;
Polynomial Polyadd(Polynomial P1,Polynomial P2)
{
Polynomial front ,rear,temp;
int sum;
rear = (Polynomial) malloc (sizeof (struct PolyNode);
front = rear;
//front这里指的是多项式链表头结点
while(P1&&P2)
{
switch (Compare(P1->expon,P2->expon))
{
case 1:
Attach(P1->coef,P1->expon,&rear);
P1 = P1->link;
break;
case-1:
Attach(P2->coef,P2->expon,&rear);
P2 = P2->link;
break;
case 0:
sum = P1->coef+P2->coef;
if(sum)Attach(sum,P1->expon,&rear);
P1 = P1->link;
P2 = P2->link;
break;
}
//还有未处理完的另一个多项式的所有结点依此复制
for(;P1;P1=P1->link)Attach(P1,P1->link,&rear);
for(;P2;P2=P2->link)Attach(P2,P2->link,&rear);
rear->link = NULL;
temp = front;
front = front->link;
free(temp);
return front;
}
}
void Attach( int c, int e, int Polynomial *pRear)
{
Polynomial P;
P = (Polynomial)malloc (sizeof (struct PolyNode));
P->coef = c;
P->expon = e;
p->link = NULL:
(*pRear)->link = P;
*pRear = P;
//修改pRear值
}
typedef struct PolyNode *Polynomial;
struct PolyNode
{
int coef;
int expon;
Polynomial link;
};
//ReadPoly()读入一个多项式
//Mult()两个多项式相乘
//Add()两个多项式相加
//PrintPoly()打印一个多项式
int main()
{
Polynomial P1,P2,PP,PS;
P1 = ReadPoly();
P2 = ReadPoly();
PP = Mult(P1,P2);
PrintPoly(PP);
PS = Add(P1,P2);
PrintPoly(PS);
return;
}
读取多项式
Polynomial ReadPoly()
{
Polynomial P,Rear,t;
int c,e,N;
scanf("%d",&N);
//链表头空结点
P = (Polynomial)malloc(sizeof(struct PolyNode));
P->link = NULL:
Rear = P;
while(N--)
{
scanf("%d %d",&c,&e);
Attach(c,e,&Rear);
//将当前项插入多项式尾部
}
//将删除临时生成的头结点
t = P;
P = P->link;
free(t);
return P;
}
void Attach(int c,int e,Polynomial *pRear)
{
Polynomial P;
P = (Polynomial)malloc (sizeof(struct PolyNode));
P->coef = c;
P->expon = e;
P->link = NULL;
(*pRear)->link = P;
*pRear = P;
}
Polynomial Mult(Polynomial P1, Polynomial P2)
{
Polynomial P,Rear,t1,t2,t;
int c,e;
if(!P1||!P2)return NULL;
t1 = P1;
t2 = P2;
P = (Polynomial)malloc(sizeof(struct PolyNode));
P->link = NULL:
Rear = P;
while(t2)
{ //先用P1的第一项乘以P2,得到P
Attach(t1->coef*t2->coef,t1->expon+t2->expon,&Rear);
t2=t2->link;
}
t1 = t1->link;
//两重循环,P1第一项和P2第一项相乘插入
while(t1)
{
t2= P2;Rear = P;
while(t2)
{
e = t1->expon+t2->expon;
c = t1->coef*t2->coef;
while(Rear->link&&Rear->link->expon>e)
Rear = Rear->link;
if(Rear->link&&Rear->link->expon==e)
{
if(Rear->link->coef==c)
Rear->link->coef+=c;
//else做删除操作
else
{
t = Rear->link;
Rear->link = t->link;
free(t);
}
}
//不相等就申请新的结点然后插入
else
{
t = (Polynomial)malloc(sizeof(struct PolyNode));
t->coef = c;
t->expon = e;
//插入过程
t->link = Rear->link;
Rear->link = t;
Rear = Rear->link;
}
t2 = t2->link;
}
t1 = t1->link;
}
//删除空结点
t2 = P;
P = P->link;
free(t2);
return P;
}
void PrintPoly(Polynomial P)
{
int flag = 0;
//flag用于调整输出格式
if(!P){printf("0 0\n");return;}
while(P)
{
if(!flag)flag = 1;
else
printf(" ");
printf("%d %d",P->coef,P->expon);
P = P->link;
}
}
typedef struct LNode* List;
LNode struct
{
ElementType Element[Maxsize];
int length;
};
有哨兵
int SequentialSearch(List Tb1,ElementType K)
{
int i;
//建立哨兵
Tb1->Element[0] = K;
for(i = Tb1->length; Tb1->Element[i] != K; i-- )
return i;
}
无哨兵
int SequentialSearch(List Tb1,ElementType K);
{
int i;
for(i = Tb1->length; i>0 && Tb1->Element[i] != K; i--)
return i;
}
int BinarySearch(List Tb1, ElementType K)
{
int mid,right,left,notFound = -1;
left = 1;
right = Tb1->length;
mid = (left + right) / 2;
while(left<=right)
{
if(Tb1->ElementType[mid]>K)
return right = mid-1;
else if(Tb1->ElementType[mid]<K)
return left = mid + 1;
//查找成功返回数组下标
else return mid;
}
return notFound;
}
每一个树都有一个根结点(root),用r表示
其余结点可以分为数个互不相交的有限集合,每个集合又是一个树,称为原来树的子树
每颗树的子树互不相交,除了根结点外,每个结点只有一个父结点,每个有N个结点的树有N-1条边
树的一些基本术语:
树的度是所有结点中最大的度数,即子孩子的个数
其他自行了解
|Element|
|Firstchild|Nextsibling|
|Left|Right|
定义:有穷的集合
性质:
typedef struct TreeNode* BinTree;
typedef BinTree Position;
struct TreeNode
{
ElementType Data;
BinTree Left;
BinTree Right;
};
访问根结点
遍历左子树
遍历右子树
void PreorderTraversal(BinTree BT)
{
if(BT)
{
printf("%d",BT->Data);
PreorderTraversal(BT->Left);
PreorderTraversal(BT->Right);
}
}
void InorderTraversal(BinTree BT)
{
if(BT)
{
InorderTraversal(BT->Left);
printf("%d",BT->Data);
InorderTraversal(BT->Right);
}
}
遍历左子树
遍历右子树
访问根结点
void PostorderTraversal(BinTree BT)
{
if(BT)
{
PostorderTraversal(Left);
PostorderTraversal(Right);
printf("%d",BT->Data);
}
}
思路:利用堆栈
void InorderTraversal(Bintree BT)
{
BinTree T = BT;
Stack S = CreatStack(MaxSize);
while(T||!isEmpty(S))
{
while(T)
{
Push(S,T);
T = T->Left;
}
if(!isEmpty(S))
{
Pop(S);
printf("%5d",T->Data);
T = T->Right;
}
}
}
层序基本过程:
先根结点入队
void LevelOrderTraversal(BinTree BT)
{
BinTree T;
Queue Q;
//若是空树直接返回
if(!BT)return;
//初始化队列
Q = CreatQueue(MaxSize);
AddQ(Q,BT);//入队
while(!Q)
{
//访问取出队列的结点
T = DeletQ(Q);
printf("%5d",T->Data);
if(T->Left)AddQ(Q,T->left);
if(T->Right)AddQ(Q,T->Right);
}
}
void InOrderTraversal(BinTree BT)
{
int Hr,Hl,Maxh;
if(BT)
{
Hl = InOrderTraversal(BT->Left);
Hr = InOrderTraversal(BT->Right);
return (Maxh+1);
}
//代表该树为空树
else return 0;
}
二叉搜索树又称为二叉排序树或二叉选择树
满足以下性质:
从查找跟结点开始,若根结点为空,直接返回NULL
若根结点不为空,根结点关键字和X进行比较
若根节点键值大于X则从左子树继续搜索
若根节点键值小于X则从右子树继续搜索
若两者相等,搜索查找完成,结束
Position Find(ElementType X,BinTree BT)
{
if(!BT)return NULL:
if (BT->Data>X)return Find(X,BT->Left);
else if (BT->Data<X)return Find(X,BT->Right);
else return BT;
//都是尾递归
}
非递归函数执行效率高,可将递归函数改为迭代函数
Position InterFind(ElementType X,BinTree BT)
{
while(BT)
{
if(BT->Data>X)BT=BT->Left;
else if (BT->Data<X)BT=BT->Right;
else return BT;
}
//树为空
return NULL;
}
//查找最小元素递归
Position FindMin(BinTree BT)
{
if(!BT)return NULL;
//找到最左结点并返回
else if(!BT->Left)return BT;
else return FindMin(BT->Left);
}
//查找最大元素迭代
Position FindMax(BinTree BT)
{
if(BT)
{
//沿右结点查找,直到最右叶结点
while(BT->Right)
BT = BT->Right;
}
return BT;
}
BinTree Insert(ElementType X,BinTree BST)
{
//若该树为空,生成并返回一个结点为一的二叉树
if(!BST)
{
BST = malloc (sizeof (struct TreeNode));
BST->Data = X;
BST->Right = BST->Left =NULL;
}
else
{
if(BST->Data>X)
BST->Left = Insert(X,BST->Left);
else if(BST->Data<X)
BST->Right = Insert(X,BST->Right);
}
return BST;
}
BinTree Delet(ElementType X,BinTree BST)
{
Position Tmp;
if(!BST)printf("未查找到");
else if(BST->Data>X)
BST->Left = Delete(X,BST->Left);
else if(BST->Data<X)
BST->Right = Delete(X,BST-Right);
//找到要删除的结点
else
{
//被删除的结点由左右两个子结点
if(BST->Left&&BST->Right)
{
Tmp = FindMin(BST->Right);
//用右子树最小值替代被删除结点
BST->Data = Tmp->Data;
BST->Right = Delete(BST->Data,BST->Right);
}
//被删除的结点有一个或无子结点
else
{
Tmp = BST;
//左边是空的,则把右边指针指向父亲结点
if(!BST->Left)
BST = BST->Right;
else if (!BST->Right)
BST = BST->Left;
free(Tmp);
}
}
return BST;
}
平衡因子BF(T)= hL-hR,其中hL和hR分别为左右子树高度
平衡二叉树:空树
或者任一结点左右子树高度差的绝对值不超过1
|BF(T)|<=1
给定结点树为n的avl树的最大高度为O(log2n)
平衡二叉树的调整:
RR旋转
LL旋转
LR旋转
RL旋转
判断被破坏点的位置确定旋转方向
typedef struct TreeNode *Tree;
struct TreeNode
{
int v;
Tree Left,Right;
//作为是否被访问过的标记
int flag;
};
int main()
{
//读入N和L
//根据第一行序列建树T
//根据树T分别判别后面L个序列是否能与T形成同一搜索树并输出结果
int N,L,i;
Tree T;
scanf("%d",&N);
while(N)
{
scanf("%d",&L);
T = MakeTree(N);
for(int i = 0; i < L; i ++)
{
if(Judge(T,N))printf("Yes\n");
else printf("No\n");
//清除T中的标记flag
ResetT(T);
}
FreeTree(T);
scanf("%d",&N);
}
return 0;
}
//建立搜素树
Tree MakeTree(int N)
{
Tree T;
int i,V;
scanf("%d",&V);
T = NewNode(V);
for(int i = 1; i < N; i ++)
{
scanf("%d",&V);
T = insert(T,V);
}
return T;
}
Tree NewNode(int V)
{
Tree T = (Tree)malloc(sizeof(struct TreeNode));
T->v = V;
T->Left = T->Right = NULL;
T->flag = 0;
return T;
}
Tree Insert(Tree T,int V)
{
if(!T)T = NewNode(V);
else
{
if(V>T->v)
T->Right = Insert(T->Right,V);
else
T->Left = Insert(T->Left,V);
}
return T;
}
int check(Tree T,int V)
{
if(T->flag)
{
if(V<T->v)
return check(T->Left,V);
else if (V>T->v)
return check(T->Right,V);
else return 0;
}
else
{
if(V==T->v)
{
T->flag = 1;
return 1;
}
else return 0;
}
}
Judge 用于判别每一个整数是否一致
有bug版本
当发现一个序列的某个数与T不一致时,必须把序列后面的数字都读完,不能直接退出,否则会被认为是下一个序列
int Judge(Tree T,int N)
{
int i,V;
scanf("%d",&V);
if(V!=T->v)return 0;
else return T->flag = 1;
for(int i = 1; i < N; i ++)
{
scanf("%d",&V);
if(!check(T,V))return 0;
}
return 1;
}
修改后
int Judge(Tree T, int N)
{
//flag 0 代表目前还一致,flag 1代表已经不一致
int V,i,flag = 0;
scanf("%d",&V);
if(V!=T->v)flag = 1;
else T->flag = 1;
for(int i = 1; i < N; i ++)
{
scanf("%d",&V);
//flag不一致时没必要进行check,一致时进行check,当flag为0即一致时,check也为0设置flag为1表示刚发现矛盾,不一致
if((!flag)&&(!check(T,V)))
flag = 1;
}
if(flag)return 0;
else return 1;
}
void ResetT(Tree T)
{
if(T->Left)ResetT(T->Left);
if(T->Right)ResetT(T->Right);
T->flag = 0;
}
void FreeTree(Tree T)
{
if(T->Left)FreeTree(T->Left);
if(T->Right)FreeTree(T->Right);
free(T);
}
Reversing Linked List
逆转链表
在单链表前添加头结点
优先队列的完全二叉树
typedef struct HeapStruct *MaxHeap;
struct HeapStruct {
//存储堆元素的数组
ElementType *Elements;
//堆当前元素个数
int Size;
//堆的最容量
int Capacity;
}
MaxHeap Creat(int Maxsize)
{
Maxheap H = malloc(sizeof (struct HeapStruct));
H->Elements = malloc((NaxSize+1)*sizeof (ElementType));
H->Size = 0;
H->Capacity = MaxSize;
H->Elements[0]=MaxData;
return H;
}
最大堆的插入
将新增结点插入到从父结点到跟结点的有序序列中
void insert(MaxHeap H,ElementType item)
{
int i;
if(isFull(i))
{
printf("最大堆已经满");
return;
}
i = ++ H->Size;
//i插入后处于堆最后一个位置
for(;H->Elements[i/2]<item;i/=2)
//向下过滤结点
H->Elements[i]=H->Elements[i/2];
//将item插入
H->[Elements] = item;
}
最大堆的删除
取出根结点最大值元素,同时删除堆的一个结点
ElementType DeletMax(MaxHeap H)
{
int Parent,Child;
ElementType MaxItem,temp;
if(IsEmpty(H))
{
printf("最大堆已空");
return;
}
//取出根结点最大值,用最大堆中最后一个元素从根结点开始向上过滤下层结点
MaxItem = H->Elements[1];
temp = H->Elements[H->Size--];
for(Parent = 1; Parent*2<=H->Size; Parent = child)
//for第二个条件用于判断是否由左儿子
{
//前两句用于将child指向左右儿子中大的一个
Child = Parent * 2;
//child!=H->Size用于判断是否有右儿子
//child==H->Size意味着左儿子是最后一个元素,无右儿子
if((Child!=H->Size)&&(H->Elements[Child]<H->Elements[Child+1]))
Child++;
if(temp >= H->Elements[Child])break;
else
H->Elements[Parent] = H->Elements[Child];
}
H->Elements[Parent] = temp;
return MaxItem;
}
最大堆的建立:
将已经存在的N个元素按照最大堆的要求存放在一个一维数组中
线性复杂度下建立最大堆
N个元素按线性存入,完全满足二叉树结构特性
调整各个结点的位置,以满足最大堆的有序性
例:将百分制成绩转换为五分制成绩
结点不同的查找频率可构造更加有效的生成树
typedef struct TreeNode *HuffmanTree;
struct TreeNode{
int Weight;
HuffmanTree Left,Right;
};
HuffmanTree Huffman(MinHeap H)
{
//假设H->Size个权值已经存在H->Elements[]->Weight中
int i;HuffmanTree T;
BuildMinHeap(H);
for(int i; i < H->Size; i ++)
{
//建立新结点
T= malloc(sizeof(struct TreeNode));
T->Left = DeleteMin(H);
T->Right = DeleteMin(H);
T->Weight = T->Left->Weight+T->Right->weight;
Insert(H,T);
}
T = DeleteMin(H);
return T;
//时间复杂性NlogN
}
哈夫曼树的特点:
如何进行不等长编码?
编码可能出现二义性,根据二义性引出前缀码的概念:
任何字符的编码都不是另一字符编码的前缀
二叉树用于编码:
左右分支0,1
字符只在叶结点上
用哈夫曼树可以实现二叉树编码代价最小
集合可以用数组存储
typedef struct {
ElementType Data;
int Parent;
}SetType;
int Find(SetType S[],ElementType X)
{
//在数组中查找值为X的元素所属集合
//MaxSize是全局变量,为数组S的最大长度
int i;
for(int i = 0; i < MaxSize && S[i].Data != X; i ++)
if(i>=MaxSize)return -1;
for(;S[i]->Parent>=0;i = S[i].Parent);
return i;
}
void Union(SetType S[],ElementType X1,ElementType X2)
{
int Root1,Root2;
Root1 = Find(S,X1);
Root2 = Find(S,X2);
if(Root1!=Root2)
S[Root2].Parent=Root1;
}
为了改善合并以后的查找性能,可以采取小的集合合并到大的集合中,可以修改union函数,即将第一个结点的Parent修改为负的元素个数,对应其绝对值即为元素个数,方便比较
#define MAXH 1001
#define MINH -10001
int H[MAXH],size;
void Creat()
{
size = 0;
H[0] = MINH;
//因为堆下标从1开始,0下标设置为岗哨,方便后面操作
}
//岗哨使得元素在0的位置停止插入
void Insert(int X)
{
int i;
for(i = ++size; H[i/2] > X; i /= 2)
H[i] = H[i/2];
H[i] = X;
}
int main()
{
int n,m,i,j,x;
scanf("%d%d",&n,&m);
//堆初始化
Creat();
for( i = 0; i < n; i ++)
{
scanf("%d",&x);
insert(x);
}
//m个需求输出
for(int i = 0; i < m; i ++)
{
scanf("%d",&j);
printf("%d",H[j]);
while(j>1)
{
//沿着根的方向输出各个结点
j/=2;
printf("%d",H[j]);
}
printf("\n");
}
return 0;
}
int Find(SetType S[],ElementType X)
{
//在数组中查找值为X的元素所属集合
//MaxSize是全局变量,为数组S的最大长度
int i;
for(int i = 0; i < MaxSize && S[i].Data != X; i ++)
if(i>=MaxSize)return -1;
for(;S[i]->Parent>=0;i = S[i].Parent);
return i;
}
这个程序中S[i].Data != X需要线性扫描,消耗许多时间
最坏的情况是每次都扫描到数组最后一个元素,反复如此O(N2)
任意有限集合N都可以被一一映射为整数0~N-1,将计算机编码-1
将集合里的每一个元素用直接用数组中的下标表示,即可取消数据域ElementType Data ,定义一个整型数组
typedef int ElementType;
//根结点下标作元素名称
typedef int SetName;
typedef ElementType SetType [MaxSize];
SetName Find(SetType S,ElementType X)
{
for(; S[X]>=0; X = S[X])
return X;
}
void Union(SetType S, SetName Root1, SetName Root2)
{
S[Root2]= Root1;
}
int main()
{
SetType S;
int n;
char in;
scanf("%d\n",&n);
Initialization(S,n);
do{
scanf("%d",&in);
switch(in)
{
case 'I':Input_Connection(S);break;
case 'C':Check_Connection(S);break;
case 'S':Check_NetWork(S,n);break;
}
}while(in != 'S');
return 0;
}
void Input_Connection(SetType S)
{
ElementType u,v;
SetName Root1,Root2;
scanf("%d %d\n",&u,&v);
Root1 = Find(S,u-1);
Root2 = Find(S,v-1);
if(Root1!=Root2)
Union(S,Root1,Root2);
}
void Check_Connection(SetType S)
{
ElementType u,v;
SetName Root1,Root2;
scanf("%d %d\n",&u,&v);
Root1 = Find(S,u-1);
Root2 = Find(S,v-1);
if(Root1==Root2)
printf("Yes\n");
else printf("No\n");
}
void Check_NetWork(SetType S,int n)
{
int i,counter=0;
for(int i = 0; i < n; i ++)
{
if(S[i]<0)counter++;
}
if(counter==1)printf("The network is connected.\n");
else printf("There are %d components.\n",counter);
}
前面Union和Find算法会超时
将树低的贴到高树上可减少树的增高
S[Root]= - 树高
if(S[Root2]<S[Root1])
S[Root1]=Root2;
else
{
if(S[Root1]==S[Root2])S[Root1]--;
S[Root2] = Root1;
}
比规模
S[Root] = - 元素个数
void Union(SetType S, SetName Root1, SetName Root2)
{
if(S[Root2]<S[Root1])
{
S[Root2]+=S[Root1];
S[Root1] = Root2;
}
else
{
S[Root1] +=S[Root2];
S[Root2] = Root1;
}
}
最坏情况下logN
此两种方法称为按秩归并
SetName Find(SetType S, ElementType X)
{
if(S[X]<0)return X;
//先找到根,再把根变成X的父结点再返回根
else return Find(S,S[X]);
}
路径压缩的优点在于,反复调用find函数时,造成时间超时,而进行一次路径压缩,下次调用find函数很合算
六度空间理论(Six Degrees of Separation)
任何两个人之间不超六个人之间认识
线性表和树可以描述为图的一个特殊情况
邻接矩阵是对称的,只需要存一半的元素即可,省去一半的存储空间
浪费空间:存稀疏图(点很多而边很少)有大量无效元素
对于稠密图,特别是稀疏图比较合算
3. 邻接表表示法
邻接表:G[N]是指针数组,对应矩阵每一行一个链表,只存非零元素
方便找任一顶点的所有”邻接点“
节约稀疏图空间
需要N个头指针和2E个结点(每个结点至少需要连个域)
void save007(Graph G)
{
for(each V in G)
{
if(!visited[G]&&FirstJump(G))
{
answer = DFS(V);
if(answer==YES)break;
}
}
if(answer==YES)output("Yes");
else output("No");
}
void DFS(Vertex V)
{
visited[V]=true;
if(IsSafe(V))answer = YES;
else
{
//for(V的每个邻接点W)
for(each W in G)
if(!visited[W]&&Jump(V,W))
{
answer = DFS(W);
if(answer==YES)break;
}
}
return answer;
}
int BFS(Vertex V)
{
visited[V] = true; count = 1;
level = 0; last = V;
Enqueue(V,Q);
while(!IsEmpty(Q))
{
V = Dequeue(Q);
for(V每个邻接点W)
if(!visited[W])
{
visited[W] = true;
Enqueue(W,Q);
count++;
tail = W;
}
if(V==last)
{
level++;
last = tail;
}
if(level == 6)break;
}
return count;
}
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv;//顶点数
int Ne;//边数
WeightType G[MaxVertexNum][MaxVertexNum];
DataType Data[MaxVertexNum];
};
typedef PtrToGNode MGraph;//以邻接矩阵存储图的类型
MGraph CreatGraph(int VertexNum)
{
MGraph Graph;
Graph = (MGraph)malloc(sizeof(struct GNode));
Graph->Nv = VertexNum;
Graph->Ne = 0;
for(V = 0; V < Graph->Nv; V ++)
for(W = 0; W < Graph->Ne; W ++)
Graph->G[V][W] = 0;
//如果有权值可以使Graph->G[V][W]=INFINITY;
return Graph;
}
typedef struct ENode *PtrToENode;
struct ENode{
Vertex V1,V2;//有向边V1,V2
WeightType Weight;//权重
};
typedef PtrToENode Edge;
//把相应的权重赋值给相应的邻接矩阵
void InsertEdge(MGraph Graph,Edge E)
{
//有向图
Graph->G[E->V1][E->V2] = E->Weight;
//若为无向图还需要插入一条V2V1边
Graph->G[E->V2][E->V1] = E->Weight;
}
int G[MAXN][MAXN],Nv,Ne;
void BuildGraph()
{
int i,j,v1,v2,w;
scanf("%d",&Nv);
for( i = 0; i < Nv; i ++)
for( j = 0; j < Nv; j ++)
G[i][j] = 0;
scanf("%d",&Ne);
for( i = 0; i < Ne; i ++)
{
scanf("%d %d %d",&v1,&v2,&w);
G[v1][v2]=w;
G[v2][v1]=w;
}
}
typedef struct GNode *PtrToGNode;
struct GNode{
int Nv;
int Ne;
AdjList G;
//邻接表
};
typedef PtrToGNode LGraph;
typedef struct Vnode{
PtrToAdjVNode FirstEdge;
DataType Data;
}AdjList[MaxVertexNum];
//AdjList 邻接表类型
typedef struct AdjVNode PtrToAdjVNode;
struct AdjVNode{
Vertex AdjV;//邻接表下标
WeightType Weight;
PtrToAdjVNode Next;
}
LGraph CreatGraph(int VertexNum)
{
Vertex V,W;
LGraph Graph;
Graph = (LGraph)malloc(sizeof(struct GNode));
Graph->Nv = VertexNum;
Graph->Ne = 0;
for( V = 0; V < Graph->Nv; V ++)
Graph->G[V].FirstEdeg = NULL;
return Graph;
}
void InsertEdge(LGraph Graph,Edge E)
{
PtrToAdjVNode NewNode;
//给V2建立新结点
NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V2;
NewNode->Weight = E->Weight;
//将V2插入V1表头
NewNode->Next = Graph->G[E->V1].FirstEdge;
Graph->G[E->V1].FirstEdge = NewNode;
//若是无向图,则需要插入V2V1边
//给V1建立新结点
NewNode = (PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
NewNode->AdjV = E->V1;
NewNode->Weight = E->Weight;
//将V1插入V2表头
NewNode->Next = Graph->G[E->V2].FirstEdge;
Graph->G[E->V2].FirstEdge = NewNode;
}
先确定根结点,然后分而治之,分别解决左右子树
void solve( int preL, int inL, int postL, int n)
{
if(n==0)return;
if(n==1)
{
post[postL]=pre[preL];return;
}
root = pre[preL];
post[postL+n-1] = root;
for(i = 0; i < n; i ++)
//找到根结点
if(in[inL+i]==root)break;
L = i;R = n-L-1;
solve(preL+1,inL,postL,L);
solve(preL+L+1,inL+L+1,postL+L,R);
}
完全二叉搜索树
两个概念:完全二叉树和二叉搜索树
树的表示方法:链表vs数组
需要操作:
填写数字(遍历)
层序遍历
完全二叉树,不浪费空间
层序遍历直接输出
void solve(int ALeft,int ARight,int TRoot)
{
n = ARight - ALeft + 1;
if(n==0)return;
//L计算出n个结点的树的左子树有多少个结点
L = GetLeftLength(n);
T[TRoot] = A[ALeft+L];
LeftTRoot = TRoot*2+1;
RightTRoot = LeftTRoot+1;
solve(ALeft,ALeft+L-1,LeftTRoot);
solve(ALeft+L+1,ARight,RightTRoot);
}
Huffman编码不唯一
注意:最优编码不一定通过Huffman算法得到
核心算法
MinHeap H = CreatHeap(N);
H = ReadHeap(N);
HuffmanTree T = Huffman(H);
int CodeLen = WPL(T,0);
int WPL(HuffmanTree T,int Depth)
{
if(!T->Left&&!T->Right)
return (Depth*T->Weight);
else
return (WPL(T->Right,Depth+1)+WPL(T->Left,Depth+1));
}
单源最短路问题:
从固定源点出发,求其到所有其他顶点的最短路径
多源最短路问题:
求任意两顶点间最短路径
void Unweighted(Vertex S)
{
Enqueue(S,Q);
while(!IsEmpty(Q))
{
V = Dequeue(Q);
for(V的每个邻接点W)
{
if(dist[W]==-1)
{
dist[W]=dist[V]+1;
path[W]=V;
Enqueue(W,Q);
}
}
}
dist[W]=S到W最短的距离
dist[S]=0
path[W]=S到W路上所经过的某个顶点
按照递增的顺序找到各个顶点间最短路
negetive cost cycle
最短路为负无穷,不考虑这种情况
void Floyd()
{
for( i = 0; i < N; i ++)
for( j = 0; j < N; j ++)
{
D[i][j] = G[i][j];
path[i][j] = -1;
}
for( k = 0; k < N; k ++)
for( i = 0; i < N; i ++)
for( j = 0; j < N; j ++)
if(D[i][k]+D[k][j]<D[i][j])
{
D[i][j] = D[i]{k}+D[k][j];
path[i][j] = k;
}
}
哈瑞波特的考试
猫变老鼠haha
老鼠变鱼hehe
猫变鱼lalala
问:哈瑞波特带谁去念的字母最少
更复杂的算法
int main()
{
MGraph G = BuildGraph();
FindAnimal(G);
return 0;
}
void FindAnimal(MGraph Graph)
{
WeightType MinDist,MaxDist,D[MaxVertexNum][MaxVertexNum];
Vertex Animal,i;
Floyd(Graph,D);
MinDist = INFINITY;
for(i = 0; i < Graph->Nv; i ++)
{
MaxDist = FindMaxDist(D,i,Graph->Nv);
if(MaxDist==INFINITY)
{
printf("0\n");
return;
}
if(MinDist>MaxDist)
{
MinDist=MaxDist;
Animal = i + 1;
}
}
printf("%d %d\n",Animal,MinDist);
}
WeightType FindAnimal(WeightType FindAnimal,Vertex i,int N)
{
WeightType MaxDist;
Vertex j;
MaxDist = 0;
for(int j = 0; j < N; j ++)
if(i!=j&&D[i][j]>MaxDist)
MaxDist=D[i][j];
return MaxDist;
}
(Minimum Spanning Tree)
一棵树
无回路
v个顶点有v-1条边
是生成树
包含全部顶点
v-1条边全部在图里
三个生成树任意加一条边都可构成回路
边的权重和最小
贪心算法(Prim和Kruskal):
贪:每一步都要最好的
好:权重最小的边
需要约束:
只用图中的边
用完V-1条边
不能有回路
void TopSort()
{
for(cnt = 0; cnt < |V|; cnt ++)
{
V = 未输出入度为0的结点
if(这样的V不存在)
{
Error("图中有回路");
break;
}
输出V,或者记录V的输出序列
for(V每个邻接点W)
Indegree[W]--;
}
}
T = O(|V2|)
随时入度为0 的顶点放为一个容器W
void TopSort()
{
for(图中每个顶点V)
if(Indegree[V]==0)
EnQueue(V,Q);
while(!IsEmpty(Q))
{
V = DeQueue(Q);
//输出V或者记录V
cnt++;
for(V每个邻接点W)
if(--Indegree[W]==0)
Enqueue(W,Q);
}
if(cnt!=|V|)
Error("图中有回路");
}
T=O(V+E)
也可以用此算法检验是否是有向无环图
void Dijkstra(Vertex s)
{
while(1)
{
V = 未收录顶点中dist最小值;
if(这样的V不存在)
break;
collected[V]=true;
for(V每个邻接点W)
if(collected[V]==false)
if(dist[V]+E<v,w><dist[W])
{
dist[W]=dist[V]+E<v,w>;
path[W]=V;
cost[W]=cost[V]+C<v,w>;
}
else if ((dist(V)+E<v,w>==dist[W])&&(cost[V]+E<v,w> < cost[W]))
{
cost[W]=cost[V]+C<v,w>;
path[W]=V;
}
}
}
void Bubble_Sort(ElementType A[],int N)
{
for(P = N-1; P >= 0; P--)
{
flag = 0;
for(i = 0; i < P; i ++)
{
if(A[i]>A[i+1])
{
swap(A[i],A[i+1]);
flag = 1;
}
}
if(flag == 1)break;
}
}
最好情况T=O(N)
最坏情况T=O(N2)
void Insertion_Sort(ElementType A[],int N)
{
for( P = 1; P < N; P ++)
{
Tmp = A[P];//摸下一张牌
for( i = P; i > 0 && A[i-1]>Tmp; i --)
A[i] = A[i-1];//移除空位
A[i] = Tmp;//新牌
}
}
最好情况 T = O(N)
最坏情况 T = O(N2)
交换两个相邻元素正好消去一个逆序对
插入排序T(N,I)= O(N+I)
如果一个序列基本有序,那么插入排序比较高效
想要提高算法效率
利用了插入排序的简单,克服了插入排序每次交换两个元素较近的缺点
同颜色的数字先被选出
void Shell_Sort(ElementType A[], int N)
{
for( D = N/2; D >= 0; D/=2)//希尔增量序列
for( P = D; P < N; P ++)
{
Tmp = A[P];
for( i = P; i >= D && A[i-D]>Tmp; i --)
A[i] = A[i-D];
A[i] = Tmp;
}
}
T = O(N2)
最坏的情况
前面三次排序未起到作用,最后一次排序起作用
增量元素不互质,则小增量可能根本不起作用
互质:元素没有公因子
void Seletion_Sort(ElementType A[], int N)
{
for( i = 0; i < N ; i ++)
{
MinPosition = ScanForMin(A,i,N-1);
//从A[i]到A[N-1]中找到最小元,赋值给MinPosition
Swap(A[i],A[MinPosition]);
//将未排序的最小元换到有序部分最后的位置
}
}
T=O(N2)
问题转换成如何快速找到最小元
利用最小堆
推到堆排序
void Heap_Sort(ElementType A[], int N)
{
BuildHeap(A);
for( i = 0; i < N; i ++)
TmpA[i] = DeleteMin(A);
for( i = 0; i < N; i ++)
A[i] = TmpA[i];
}
void Heap_Sort(ElementType A[], int N)
{
for(i = N/2; i >=0; i --)
PercDown(A,i,N);//BuildHeap
for(i = N - 1; i > 0; i --)
{
Swap(&A[0],&A[i]);//delete MAX
PercDown(A,0,i);
}
}
void Merge(ElementType A[],ElementType TmpA[],int L,int R,int RightEnd)
{
LeftEnd = R - 1;//左边终点位置
Tmp = L;//存放数组的初始位置
NumElements = RightEnd - L + 1;
while(L<=LeftEnd&&R<=RightEnd)
{
if(A[L]<=A[R])TmpA[Tmp++] = A[L++];
else TmpA[Tmp++] = A[R++];
}
//直接复制左右位置剩下的
while(L<=LeftEnd)TmpA[Tmp++] = A[L++];
while(L<=RightEnd)TmpA[Tmp++] = A[R++];
//将tmpa中数据返回A中
for(i = 0; i < NumElements; i ++, RightEnd --)
A[RightEnd] = TmpA[RightEnd];
}
void MSort(ElementType A[],ElementType TmpA[],int L,int RightEnd)
{
int Center;
if(L<RightEnd)
{
Center =(L + RightEnd)/2;
MSort(A,TmpA,L,Center);
MSort(A,TmpA,Center+1,RightEnd);
Merge(A,TmpA,L,Center+1,RightEnd);
//原始数组,新数组,左边起点,右边起点,右边终点
}
}
时间复杂度:左边T(N/2)右边T(N/2)
T(N) = T(N/2)+T(N/2)+O(N)
T(N) = O(NlogN)
void Merge_sort( ElementType A[], int N)
{
ElementType *TmpA;
TmpA = malloc (N*sizeof (ElementType));
if(TmpA != NULL)
{
MSort(A,TmpA,0,N-1);
free(TmpA);
}
else Error("空间不足");
}
//length是当前有序子列的长度
void Merge_Sort(ElementType A[],ElementType TmpA[], int N, int length)
{
Merge1将A中元素归并到TmpA,最后不进行for循环
for(i = 0; i <= N - 2*length; i += 2*length)
Merge1(A,TmpA,i,i+length,i+length*2-1);
//归并最后两个子列
if(i+length<N)
Mergr1(A,TmpA,i,i+length,N-1);
//子列就剩一个
else
for(j = i; j < N; j ++)
TmpA[j] = A[j];
}
void Merge_Sort(ElementType A[],int N)
{
int length = 1;
ElementType *TmpA;
TmpA = malloc(N*sizeof(ElementType));
if(TmpA!=NULL)
{
while(length<N)
{
Merge_pass(A,TmpA,N,Length);
length*=2;
Merge_pass(TmpA,A,N,Length);
length*=2;
}
free(TmpA);
}
else Error("空间不足");
}
缺点得重新申请一个空间
ElementType Median3(ElementType A[], int Right, int Left)
{
int Center = (Left+Right)/2;
if(A[Left]>A[Center])
Swap(&A[Left],&A[Center]);
if(A[Left]>A[Right])
Swap(&A[Left],&A[Right]);
if(A[Center]>A[Right])
Swap(&A[Center],&A[Right]);
//A[Left]<=A[Center]<=A[Right]
Swap(&A[Center],&A[Right-1]);
return A[Right-1];返回主元
}
停下来交换
void QuickSort(ElementType A[],int Left,int Right)
{
if(Cutoff<=Right - Left)
{
Pivot = Median3(A,Left,Right);
i = Left;
j = Right - 1;
for(;;)
{
while(A[i++]<Pivot)
while(A[j++]>Pivot)
if(i<j)Swap(&A[i],&A[j]);
else break;
}
Swap(&A[i],&A[Right - 1]);
QuickSort(A,Left,i - 1);
QuickSort(A,i+1,Right);
}
else
Insertion_Sort(A+Left,Right - Left + 1);
}
void Quick_Sort(ElementType A[],int N)
{
QuickSort(A,0,N-1);
}
复杂度分析
最好的情况:初始即有序
最坏的情况:
有[N/2]个环,每个环包含两个元素
查找的本质:已知对象找位置
有序安排对象:全序,半序
直接算出对象的位置:散列
散列查找法两项基本工作:
计算位置冲突:构造散列函数确定关键词的存储位置
解决冲突:应用某种策略,解决多个关键字位置相同的问题
时间复杂度几乎是常量:O(1),即查找时间与问题的规模无关
散列基本思想:
Index Hash(const char *Key,int TableSize)
{
unsigned int h = 0;
while(* Key != '\0')
h = (h<<5)+*Key++;
return h % TableSize;
}
散列表性能分析:
成功平均查找长度(ASLs)
不成功平均查找长度(ASLu)
散列函数:h(key)= key mod 11
程序实现
typedef struct HashTbl*HashTable;
struct HashTbl{
int TableSize;
Cell*TheCells;
}H;
HashTable InitializeTable(int TableSize)
{
HashTable H;
int i;
if(TableSize<MinTableSize)
{
Error("散列表太小");
return NULL;
}
H = (HashTable)malloc(sizeof (struct HashTbl));
if(H==NULL)
FatalError("空间溢出");
H->TableSize = NextPrime(TableSize);
H->TheCells = (Cell*)malloc(sizeof(Cell)*H->TableSize);
if(H->TheCells==NULL)
FatalError("空间溢出");
for(i = 0; i < H->TableSize; i ++)
{
H = TheCell[i].Info = Empty;
}
return H;
}
在开放地址散列表中,删除操作要很小心,通常只能“懒惰操作”,即需要增加一个“删除标记”,而不是真正的删除它,以便查找时不会“断链”,其他空间可以在下次插入时重用
当装填因子过大,解决方法是加倍扩大散列表,这个过程叫做再散列
typedef struct ListNode *Position,*List;
struct ListNode{
ElementType Element;
Position Next;
};
typedef struct HashTbl *HashTable;
struct HashTbl{
int TableSize;
List TheLists;
};
Position Find(ElementType Key,HashTable H)
{
Position P;
int Pos;
Pos = Hash(Key,H->TableSize);//初始散列位置
P = H->TheList[Pos].Next;//获得链表头
while(P!=NULL&&strcmp(P->Element,Key));
P=P->Next;
return P;
}
int main()
{
int TableSize;//散列表大小估计
int wordcount = 0,length = 0;
HashTable H;
ElementType word;
FILE *fp;
char document[30] = "HarryPotter.txt";
H = Initialize Table(TableSize);//建立散列表
if((fp=fopen(document,"r"))==NULL)FatalError("无法打开文件!\n");
while(!feof(fp))
{
length = GetAWord(fp,word);
//从文件中读取一个单词
if(length>3)
{
wordcount++;
InsertAndCount(word,H);
}
}
fclose(fp);
printf("该文档中出现了%d个有效单词",wordcount);
Show(H,10.0/100);//显示前百分之10
DestroyTable(H);//销毁散列表
return 0;
}
int main()
{
创建散列表
读入号码插入表中
扫描表输出狂人
return 0;
}
int main()
{
int N,i;
ElementType Key;
HashTable H;
scanf("%d",&N);
H = CreatTable(N*2);//创建散列表
for(int i = 0; i < N; i ++)
{
scanf("%s",Key);Insert(H,Key);
scanf("%s",Key);Insert(H,Key);
}
ScanAndOutput(H);
DestoryTable(H);
return 0;
}
void ScanAndOutput(HashTable H)
{
int i,MaxCnt = PCnt = 0;
ElementType MinPhone;
List Ptr;
MinPhone[0] = '\0';
//扫描链接
for( i = 0; i < H->TableSize; i ++)
{
Ptr = H->Head[i].Next;
while(Ptr)
{
if(Ptr->Count>MaxCnt)//更新最大通话次数
{
MaxCnt = Ptr->Count;
strcpy(MinPhone,Ptr->Data);
PCnt = 1;
}
else if (Ptr->Count==Maxcnt)
{
PCnt++;//狂人计数
if(strcmp(MinPhone,p->Count)>0)
strcpy(MinPhone,Ptr->Data);
//更新狂人最小号码
}
Ptr = Ptr->Next;
}
}
printf("%s %d",MinPhone,MaxCnt);
if(PCnt>1)printf("%d",PCnt);
printf("\n");
}
从左向右扫描,直到发现顺序不对,跳出循环
从跳出点继续向右扫描,与原始序列比对,发现不同则判断非
循环自然结束,则判断为是,返回跳出地点
判断归并段长度
判断连接点的前后顺序
2. Sort with Swap(0,*)
给定0到N-1数字,利用0来排序
只有一个元素:不需要交换
环里有n0个元素,包括0:需要n0-1次元素
第i个环里有ni个元素,不包括0:先把0换到环里,再进行(ni+1)-1次交换,一共是ni+1次交换
若N个元素的序列中包含S个单元环,K个多元环,则交换次数为:
3. Hashing Hard Version
给出散列映射结果,反求输出顺序
若x映射到H(x)若该位置已经有y了,则y一定是x之前被输入的
散列映射和拓扑排序结合
模式匹配:
#include
#include
typedef char* Position;
int main(){
char string[] = "This is a simple example";
char pattern[] = "simple";
Position p = strstr(string,pattern);
printf("%s\n",p);
return 0;
}
#include
#include
typedef char* Position;
#define NotFound NULL
int main(){
char string[] = "This is a simple example";
char pattern[] = "sample";
Position p = strstr(string,pattern);
if(p==NotFound)printf("Not Found.\n");
else printf("%s\n",p);
return 0;
}
#include
#include
typedef int Position;
#define NotFound -1
int main(){
char string[] = "This is a simple example.";
char pattern[] = "sample";
Position p = KMP(string,pattern);
if(p==NotFound)printf("Not Found.\n");
else printf("%s\n",string+p);
return 0;
}
Position KMP(char *string,char *pattern)
{
int n = strlen(string);//O(n)
int m = strlen(pattern);//O(m)
int s,p*match;
match = (int*)malloc(sizeof(int)* m);
BuildMatch(pattern,match);
s = p = 0;
while(s<n && p<m)
{
if(string[s] = pattern[p])
{
s++;
p++;
}
else if(p>0)
p=match(p-1)+1;
else s++;
}
return(p==m)?(s-m):NotFound;
}
void BuildMatch(char*pattern,int *match)
{
int i,j;
int m = strlen(pattern);
match[0]=-1;
for( j = 0; j < m; j ++)
{
i = match[j-1];
while((i>=0)&&(pattern[i+1]!=patern[j]))
i = match[i];
if(pattern[i+1] = pattern[j])
match[j] = i+1;
else match[j] = -1;
}
}