- 拼题A(Programming Teaching Assistant):https://pintia.cn/ 本课程的编程练习将在这里布置。
- PAT(Programming Ability Test)官网:https://www.patest.cn/ 提供全部考试真题。
- 1、视频(B站),B站课程链接
- 2、视频中习题,讨论,PPT(中国大学MOOC) 网课地址
- 3、 笔记整理
- 4、完成编程作业
######################################################
#include
// 传入一个正整数N
// 顺序打印1到N的全部整数
void PrintN1(int N);
void PrintN2(int N);
// N = 100,1000,10000,1000000,......
int main() {
int N;
scanf("%d", &N);
//PrintN1(N);
PrintN2(N);
return 0;
}
// 方法一:循环实现
void PrintN1(int N) {
int i;
for (i = 1; i <= N; i++) {
printf("%d\n", i);
}
return;
}
//方法二:递归实现,占用空间大
void PrintN2(int N) {
if (N) {
PrintN2(N - 1);
printf("%d\n", N);
}
return;
}
-------------------------------------------------------
#include
#include
// 例3: 计算给定多项式在给定点x处的值。
// f(x) = a0 + a1*x + a2*x^2+...+an*x^n
int main() {
//
return 0;
}
// 直观解法
double f1(int n, double a[], double x) {
int i;
double p = a[0];
for (i = 1; i <= n; i++)
p += (a[i] * pow(x, i));
return p;
}
// 提升解法 f(x)= a0 + x(a1 + x(...a(n-1)+x*an)) 将x提取
double f2(int n, double a[], double x) {
int i;
double p = a[n];
for (i = n; i > 0; i--)
p = a[i - 1] + x * p;
return p;
}
/*计算程序运行时长 模块
*/
#include
#include
clock_t start, stop; //clock_t是clock()函数返回的类型变量
double duration; // 记录被测函数运行时间,以秒为单位
int main() {
/*无需计时部分 */
start = clock(); //开始计时
MyFunction();
stop = clock(); //结束计算
/*无需计时部分 */
duration = ((double)(stop - start)) / CLK_TCK;
return 0;
}
/*计算程序运行时长 实例
*/
#include
#include
#include
clock_t start, stop; //clock_t是clock()函数返回的类型变量
double duration; // 记录被测函数运行时间,以秒为单位
#define MAXN 10 //多项式最大项数,即多项式阶数+1
#define MAXK 1e7 // 被测函数最大重复调用次数
double f1(int n, double a[], double x);
double f2(int n, double a[], double x);
int main() {
int i;
double a[MAXN]; //存储多项式的系数
for (i = 0; i < MAXN; i++) a[i] = (double)i;
start = clock(); //开始计时
for (i = 0; i < MAXK; i++) //重复调用函数以获得充分多的时钟打点数
f1(MAXN - 1, a, 1.1);
stop = clock(); //结束计算
duration = ((double)(stop - start)) / CLK_TCK/MAXK;
printf("ticks1 = %f\n", (double)(stop - start));
printf("duration1 = %6.2e\n", duration);
start = clock(); //开始计时
for (i = 0; i < MAXK; i++)
f2(MAXN - 1, a, 1.1);
stop = clock(); //结束计算
duration = ((double)(stop - start)) / CLK_TCK/MAXK;
printf("ticks2 = %f\n", (double)(stop - start));
printf("duration2 = %6.2e\n", duration);
return 0;
}
double f1(int n, double a[], double x) {
int i;
double p = a[0];
for (int i = 1; i <= n; i++)
p += (a[i] * pow(x, i));
return p;
}
double f2(int n, double a[], double x) {
int i;
double p = a[n];
for (i = n; i > 0; i--)
p = a[i - 1] + x * p;
return p;
}
/*计算程序运行时长 实例
*/
#include
#include
#include
clock_t start, stop; //clock_t是clock()函数返回的类型变量
double duration; // 记录被测函数运行时间,以秒为单位
#define MAXN 101 //多项式最大项数,即多项式阶数+1
#define MAXK 1e7 // 被测函数最大重复调用次数
double f1(int n, double a[], double x);
double f2(int n, double a[], double x);
int main() {
int i;
double a[MAXN]; //存储多项式的系数
a[0] = 1;
for (i = 1; i < MAXN; i++) a[i] = (double)i;
start = clock(); //开始计时
for (i = 0; i < MAXK; i++) //重复调用函数以获得充分多的时钟打点数
f1(MAXN - 1, a, 1.1);
stop = clock(); //结束计算
duration = ((double)(stop - start)) / CLK_TCK/MAXK;
printf("ticks1 = %f\n", (double)(stop - start));
printf("duration1 = %6.2e\n", duration);
start = clock(); //开始计时
for (i = 0; i < MAXK; i++)
f2(MAXN - 1, a, 1.1);
stop = clock(); //结束计算
duration = ((double)(stop - start)) / CLK_TCK/MAXK;
printf("ticks2 = %f\n", (double)(stop - start));
printf("duration2 = %6.2e\n", duration);
return 0;
}
// f(x) = 1 + x + x^2/2 + ... + x^i/i + ... + x^100/100
double f1(int n, double a[], double x) {
int i;
double p = a[0];
for (int i = 1; i <= n; i++)
p += pow(x, i)/a[i] ;
return p;
}
double f2(int n, double a[], double x) {
int i;
double p = 1/a[n];
for (i = n; i > 0; i--)
p = 1/a[i - 1] + x * p;
return p;
}
- 1、空间复杂度S(n) : 占用存储单元的长度
- 2、时间复杂度T(n): 耗费时间长度
例3中:
double f1(int n, double a[], double x) {
int i;
double p = a[0];
for (int i = 1; i <= n; i++)
p += (a[i] * pow(x, i));
return p;
}
double f2(int n, double a[], double x) {
int i;
double p = a[n];
for (i = n; i > 0; i--)
p = a[i - 1] + x * p;
return p;
}
- 最坏情况复杂度 T worst(n), 更关心这个指标
- 平均复杂度 T avg(n)
不管以2还是10为底,都是相差常数倍。
算法1:
#include
int MaxSubseqSum1(int A[], int N)
{
int ThisSum, MaxSum = 0;
int i, j, k;
for (i = 0; i < N; i++) { //i为子列左端位置
for (j = i; j < N; j++) {//j为子列右端位置
ThisSum = 0; // A[i]到A[j]的子列和
for (k = i; k <= j; k++)
ThisSum += A[k];
if (ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}
int main() {
int A[] = { 1,-2,3, 5, 6,100,-5 };
int a = MaxSubseqSum1(A, 7);
printf("%d", a);
return 0;
}
算法2:
#include
int MaxSubseqSum2(int A[], int N)
{
int ThisSum, MaxSum = 0;
int i, j;
for (i = 0; i < N; i++) { //i为子列左端位置
ThisSum = 0; // A[i]到A[j]的子列和
for (j = i; j < N; j++) {//j为子列右端位置
ThisSum += A[j]; //对于相同的i,不同的j,只要在j-1次循环的基础上累加一项
if (ThisSum > MaxSum)
MaxSum = ThisSum;
}
}
return MaxSum;
}
int main() {
int A[] = { 1,-2,3, 5, 6,100,-5 };
int a = MaxSubseqSum2(A, 7);
printf("%d", a);
return 0;
}
算法3:分而治之
#include
int Max3(int A, int B, int C){//返回三个整数中的最大值
return A > B ? A > C ? A : C : B > C ? B : C;
}
int DivideAndConquer(int List[], int left, int right) {//分治法求List[left]到List[right]的最大子列和
int MaxLeftSum, MaxRightSum;
int MaxLeftBorderSum, MaxRightBorderSum; //存放跨分界线的结果
int LeftBorderSum, RightBorderSum;
int center, i;
if (left == right) {//递归终止条件,子列只有一个数字
if (List[left] > 0) return List[left];
else return 0;
}
//分
center = (left + right) / 2;
//递归求两边子列的最大和
MaxLeftSum = DivideAndConquer(List, left, center);
MaxRightSum = DivideAndConquer(List, center + 1, right);
//求跨分界线的最大子列和
MaxLeftBorderSum = 0; LeftBorderSum = 0;
for (i = center; i >= left; i--) {//从中线向左扫描
LeftBorderSum += List[i];
if (LeftBorderSum > MaxLeftBorderSum)
MaxLeftBorderSum = LeftBorderSum;
}
MaxRightBorderSum = 0; RightBorderSum = 0;
for (i = center + 1; i <= right; i++) { //从中线向右扫描
RightBorderSum += List[i];
if (RightBorderSum > MaxRightBorderSum)
MaxRightBorderSum = RightBorderSum;
}
//返回治的结果
return Max3(MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum);
}
int MaxSubseqSum3(int List[], int N)
{
return DivideAndConquer(List,0,N-1);
}
int main() {
int A[] = { 1,-2,3, 5, 6,100,-5 };
int a = MaxSubseqSum3(A, 7);
printf("%d", a);
return 0;
}
算法4: 在线处理
#include
int MaxSubseqSum4(int A[], int N)
{
int i;
int ThisSum =0, MaxSum = 0;
for (i = 0; i < N; i++) { //i为子列左端位置
ThisSum += A[i]; // 向右累加
if (ThisSum > MaxSum)
MaxSum = ThisSum;
else if (ThisSum < 0) //如果当前子列和为负
ThisSum = 0; //不可能使后面的部分和增大,抛弃
}
return MaxSum;
}
int main() {
int A[] = { 1,-2,3, 5, 6,100,-5 };
int a = MaxSubseqSum4(A, 7);
printf("%d", a);
return 0;
}
多项式问题
//链表结构存储非零项
typedef struct PolyNode* Polynomial;
struct PolyNode {
int coef;
int expon;
Polynomial ink;
};
线性表: 由同类型数据元素构成有序序列的线性结构。
6种基本操作:初始化,查找元素,返回位置,插入,删除,返回长度。
typedef struct LNode* List;
struct LNode {
ElementType Data[MAXSIZE];
int Last;
};
struct LNode L;
List PtrL;
/* 初始化*/
List MakeEmpty() {
List PtrL;
PtrL = (List)malloc(sizeof(struct LNode));
PtrL->Last = -1;
return PtrL;
}
/*查找*/
int Find(List PtrL, ElementType X) {
int i = 0;
while (i <= PtrL->Last && PtrL->Data[i] != X)
i++;
if (i > PtrL->Last) return -1; //没找到
else return i; //返回存储位置
}
/* 3、插入*/
void Inert(ElementType X, int i, List PtrL) {
int j;
if (PtrL->Last == MAXSIZE - 1) {
printf(" 表满 ");
return;
}
if (i < 1 || i>PtrL->Last + 2) {
printf(" 位置不合法 ");
return;
}
for (j = PtrL->Last; j >= i - 1; j--)
PtrL->Data[j + 1] = PtrL->Data[j]; // 将ai~an倒序向后移动
PtrL->Data[i - 1] = X; //新元素插入
PtrL->Last++; // Last仍指向最后元素
return;
}
// 删除
void Delete(int i, List PtrL) {//下标从1开始
int j;
if (i<1 || i > PtrL->Last+1) {
printf("位置%d不存在元素", i);
return;
}
for (j = i; j <= PtrL->Last; j++)
PtrL->Data[j - 1] = PtrL->Data[j]; //后面的元素依次前移
PtrL->Last--; /*Last仍指向最后元素*/
return;
}
-------------------------------------------
--------------------------------------------------------------------------
附录代码1:
typedef int Position;
typedef struct LNode* List;
struct LNode {
ElementType Data[MAXSIZE];
Position Last;
};
/* 初始化*/
List MakeEmpty() {
List L; // PPT里L==> PtrL
L = (List)malloc(sizeof(struct LNode));
L->Last = -1;
return L;
}
/*查找*/
#define ERROR -1
Position Find(List L, ElementType X) {
Position i = 0;
while (i <= L->Last && L->Data[i] != X)
i++;
if (i > L->Last) return ERROR; //没找到
else return i; //返回存储位置
}
/* 插入 */
bool Insert(List L, ElementType X, Position P) {//P从零开始
Position i;
if (L->Last == MAXSIZE - 1) {//表空间已满,不能插入
printf("表满");
return false;
}
if (P<0 || P > L->Last + 1) {
printf("位置不合法");
return false;
}
for (i = L->Last; i >= P; i--)
L->Data[i + 1] = L->Data[i]; //将位置P及以后的元素向后移
L->Data[P] = X; //新元素插入
L->Last++; /* Last仍指向最后元素 */
return true;
}
// 删除
bool Delete(List L, Position P) {//P下标从0开始
Position i;
if (P<0 || P > L->Last) {
printf("位置%d不存在元素", P);
return false;
}
for (i = P + 1; i <= L->Last; i++)
L->Data[i - 1] = L->Data[i]; // 将后面的元素往前移动。
L->Last--; /*Last仍指向最后元素*/
return true;
}
typedef struct LNode* List;
struct LNode {
ElementType Data;
List Next;
};
struct LNode L;
List PtrL;
1、求表长
/*求表长*/
int Length(List PtrL) {
List p = PtrL; //p指向表的第一个结点
int j = 0;
while (p) {
p = p->Next;
j++;
}
return 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 p;
else return NULL;
}
(2) 按值查找
/*按值查找*/
List Find(ElementTYpe X, List PtrL) {
List p = PtrL;
while (p != NULL && p->Data != X)
p = p->Next;
return p;
}
3、插入
(1)构造一个新结点,用s指向
(2)找到链表的第i-1个结点,用p指向
(3)修改指针,插入结点
/*插入操作*/
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;
}
p = FindKth(i - 1, PtrL); //查找第i-1个结点
if (p == NULL) {
printf(" 参数i错 ");
return NULL;
}
else {
s = (List)malloc(sizeof(struct LNode));
s->Data = X;
s->Next = p->Next;
p->Next = s;
return PtrL;
}
}
4、删除
(1)先找到链表的第i-1个结点,用p指向
(2)再用指针s指向要被删除的结点(p的下一个结点)
(3)然后修改指针,删除s所指结点
(4)最后释放s所指结点的空间
/*删除操作*/
List Delete(int i, List PtrL) {
List p, s;
if (i == 1) { /* 要删除的是第一个结点*/
s = PtrL; /* s 指向第1个结点 */
if (PtrL != NULL) PtrL = PtrL->Next; /* 从链表中删除 */
else return NULL;
free(s); // 释放被删除的结点
return PtrL;
}
p = FindKth(i - 1, PtrL); //查找第i-1个结点
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 LNode* PtrToLNode;
struct LNode {
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode Position;
typedef PtrToNode List;
/* 查找 */
#define EROOR NULL
Position Find(List L, ElementType X) {
Position p = L; //p指向L的第一个结点
while (p && p->Data != X)
p = p->Next;
//return p;
if (p) return p;
else
return ERROR;
}
bool Insert(List L, ElementType X, Position P) {//默认L有头结点
Position tmp, pre;
//查找P的前一个结点
for (pre = L; pre && pre->Next != P; pre = pre->Next);
if (pre == NULL) {//P所指的结点不在L中
printf("插入位置参数错误\n");
return false;
}
else {/* 找到P的前一个结点pre ,在P前插入新结点
*/
tmp = (Position)malloc(sizeof(struct LNode));
tmp->Data = X;
tmp->Next = P;
pre->Next = tmp;
return true;
}
}
/* 带头结点的删除 */
bool Delete(List L, Position P) {
Position pre;
//查找P的前一个结点
for (pre = L; pre && pre->Next != P; pre = pre->Next);
if (pre == NULL || P == NULL) {
printf("删除位置参数错误\n");
return false;
}
else {/* 找到了P的前一个结点pre,将P位置的结点删除*/
pre->Next = p->Next;
free(P);
return true;
}
}
----------------------------------------------------
----------------------------------------------------
- 线性表:n个元素都是基本的单元素
- 广义表:元素不仅可以是单元素也可以是另一个广义表。
/*广义表*/
typedef struct GNode* GList;
struct GNode {
int Tag; /* 标志域: 0表示结点是单元素, 1表示结点是广义表*/
union { // 共用存储空间
ElementType Data;
GList SubList;
}URegion;
GList Next;
};
多重链表: 链表中的结点可能同时隶属于多个链
- 只存储矩阵非0元素项:
结点的数据域:行坐标Row, 列坐标Col,数值Value- 两个指针域
行指针 Right
列指针 Down
- 生成空堆栈
- 判断堆栈是否已满
- 压入
- 是否为空
- 删除并返回栈顶元素。
#define MaxSize
typedef struct SNode* Stack;
struct SNode {
ElementType Data[MaxSize];
int Top;
};
/* 入栈 */
void Push(Stack PtrS, ElementType item) {
if (PtrS->Top == MaxSize - 1) {
printf("堆栈满"); return;
}
else {
PtrS->Data[++(PtrL->Top )] = item;
return;
}
}
/* 出栈 */
ElementType Pop(Stack PtrS) {
if (PtrS->Top == -1) {
printf("堆栈空");
return ERROR;
}
else {
return (PtrS->Data[(PtrS->Top)--]);
}
}
/* 用一个数组实现两个堆栈 */
#define MaxSize<存储数据元素的最大个数>
struct DStack {
ElementType Data[MaxSize];
int Top1; // 堆栈1的栈顶指针
int Top2; // 堆栈2的栈顶指针
} S;
S.Top1 = -1;
S.Top2 = MaxSize;
void Push(struct DStack* PtrS, ElementType item, int Tag) {
/* Tag 作为区分两个堆栈的标志,取值为1和2*/
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 DStack* 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) {//堆栈2空
printf("堆栈2空"); return NULL;
}
else return PtrS->Data[(PtrS->Top2)++];
}
}
typedef struct SNode* Stack;
struct SNode {
ElementType Data;
struct SNode* Next;
};
/* 堆栈初始化 */
Stack CreateStack() {
//构建一个堆栈的头结点,返回指针
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
/* 判断堆栈 S是否为空 */
int IsEmpty(Stack S) {//若为空返回1
return (S->Next == NULL);
}
/* 将元素item 压入堆栈S */
void Push(ElementType item, Stack S) {
struct SNode* TmpCell;
TmpCell = (struct SNode*)malloc(sizeof(struct SNode));
TmpCell->Element = item;
TmpCell->Next = S->Next;
S->Next = TmpCell;
}
/* 删除并返回堆栈S的栈顶元素*/
ElementType Pop(Stack S) {
struct SNode* FirstCell;
ElementType TopElem;
if (IsEmpty(S)) {
printf("堆栈空"); return NULL;
}
else {
FirstCell = S->Next;
S->Next = FirstCell->Next;
TopElem = FirstCell->Element;
free(FirstCell);
return TopElem;
}
}
附录代码
/* 附录代码 1*/
typedef int Position;
struct SNode {
ElementType* Data; //存储元素的数组
Position Top; // 栈顶指针
int MaxSize; //堆栈最大容量
};
typedef struct SNode* Stack;
Stack CreateStack(int MaxSize) {
Stack S = (Stack)malloc(sizeof(Struct SNode));
S->Data = (ElementType*)malloc(MaxSize * sizeof(ElementType));
S->Top = -1;
S->MaxSize = MaxSize;
return S;
}
bool IsFull(Stack S) {
return (S->Top == S->MaxSize - 1);
}
bool Push(Stack S, ElementType X) {
if (IsFull(S)) {
printf("堆栈满");
return false;
}
else {
S->Data[++(S->Top)] == X;
return true;
}
}
bool IsEmpty(Stack S) {
return (S->Top == -1);
}
ElementType Pop(Stack S) {
if (IsEmpty(S)) {
printf("堆栈空");
return ERROR; //ERROR是ElementType的特殊值,标志错误
}
else
return (S->Data[(S->Top)--]);
}
/* 附录代码2*/
typedef struct SNode *PtrToSNode;
struct SNode {
ElementType Data;
PtrToSNode Next;
};
typedef PtrToSNode Stack;
Stack CreateStack() {//构建一个堆栈的头结点,返回该结点指针
Stack S;
S = (Stack)malloc(sizeof(struct SNode));
S->Next = NULL;
return S;
}
bool IsEmpty(Stack S) {//判断堆栈S是否为空,若是返回true,否则返回false
return (S->Next == NULL);
}
bool Push(Stack S, ElementType X) {//将元素压入堆栈S
PtrToSNode TmpCell;
TmpCell = (PtrToSNode)malloc(sizeof(struct SNode));
TmpCell->Data = X;
TmpCell->Next = S->Next;
S->Next = TmpCell;
return true;
}
ElementType Pop(Stack S) {//删除并返回堆栈S的栈顶元素
PtrToSNode FirstCell;
ElementType TopElem;
if (IsEmpty(S)) {
printf("堆栈空");
return ERROR;
}
else {
FirstCell = S->Next;
TopElem = FirstCell->Data;
S->Next = FirstCell->Next;
free(FirstCell);
return TopElem;
}
}
#define MaxSize <存储数据元素的最大个数>
struct QNode {
ElementType Data[MaxSize];
int rear;
int front;
};
typedef struct QNode* Queue;
标记队列满的情况
(1) 使用额外标记,Size或者tag(最后为插入还是删除)
(2) 仅使用n-1个数组空间。
(1) 入队列
/* 入队列 */
void AddQ(Queue PtrQ, ElementType item) {
if ((PtrQ->rear + 1) % MaxSize == PtrQ->front) {
printf("队列满");
return;
}
PtrQ->rear = (PtrQ->rear + 1) % MaxSize;
PtrQ->Data[PtrQ->rear] = item;
}
/* 出队列 */
ElementType DeleteQ(Queue PtrQ) {
if (PtrQ->front == PtrQ->rear) {
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 DeleteQ(Queue PtrQ) {
struct Node* FrontCell;
ElementType FrontElem;
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;
FrontElem = FrontCell->Data;
free(FrontCell); /* 释放被删除结点的空间*/
return FrontElem;
}
讨论2.3 如何用两个堆栈模拟实现一个队列?
如何用两个堆栈模拟实现一个队列? 如果这两个堆栈的容量分别是m和n(m>n),你的方法能保证的队列容量是多少?
附件代码
/* 附件代码1 */
typedef int Position;
struct QNode {
ElementTYpe* Data; //存储元素的数组
Position Front, Rear; // 队列的头、尾指针
int MaxSize; // 队列最大容量
};
typedef struct QNode* Queue;
Queue CreateQueue(int MaxSize) {
Queue Q = (Queue)malloc(sizeof(struct QNode));
Q->Data = (ElementType*)malloc(MaxSize * sizeof(ElementType));
Q->Front = Q->Rear = 0;
Q->MaxSize = MaxSize;
return Q;
}
bool IsFull(Queue Q) {
return ((Q->Rear + 1) % Q->MaxSize == Q->Front);
}
bool AddQ(Queue Q, ElementType X) {
if (IsFull(Q)) {
printf("队列满");
return false;
}
else {
Q->Rear = (Q->Rear + 1) % Q->MaxSize;
Q->Data[Q->Rear] = X;
return true;
}
}
bool IsEmpty(Queue Q) {
return (Q->Front == Q->Rear);
}
ElementType DeleteQ(Queue Q) {
if (IsEmpty(Q)) {
printf("队列空");
return ERROR;
}
else {
Q->Front = (Q->Front + 1) % Q->MaxSize;
return Q->Data[Q->Front];
}
}
/* 附件代码2 */
typedef struct Node* PtrToNode;
struct Node { //队列中的结点
ElementType Data;
PtrToNode Next;
};
typedef PtrToNode Position;
struct QNode {
Position Front, Rear; /* 队列的头、尾指针*/
int MaxSize; //队列的最大容量
};
typedef struct QNode* Queue;
bool IsEmpty(Queue Q) {
return (Q->Front == NULL);
}
ElementType DeleteQ(Queue Q) {
Position FrontCell;
ElementType FrontElem;
if (IsEmpty(Q)) {
printf("队列空");
return ERROR;
}
else {
FrontCell = Q->Front;
if (Q->Front == Q->Rear) /* 队列只有一个元素 */
Q->Front = Q->Rear = NULL; //删除后队列置为空
else
Q->Front = Q->Front->Next;
FrontElem = FrontCell->Data;
free(FrontCell); /* 释放被删除的结点空间 */
return FrontElem;
}
}
/* 多项式加法 */
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: //P1中的数据项指数较大
Attach(P1->coef, P1->expon, &rear);
P1 = P1->link;
break;
case -1: // P2大
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->coef, P1->expon, &rear);
for (; P2; P2 = P2->link) Attach(P2->coef, P2->expon, &rear);
rear->link = NULL;
temp = front;
front = front->link; /* 令front 指向结果多项式第一个非零项*/
free(temp); /* 释放临时空表头结点*/
return front;
}
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;
}
数组:
- 编程简单,调试容易
- 需要事先确定数组大小
链表:
- 动态性强
- 编程略为复杂、调试比较困难。
/* 数据结构设计*/
typedef struct PolyNode* Polynomial;
struct PolyNode {
int coef;
int expon;
Polynomial link;
};
int main() {
Polynomial P1, P2, PP, PS;
P1 = ReadPoly();
P2 = ReadPoly();
PP = Mult(P1, P2);
PrintPoly(PP);
PS = Add(P1, P2);
PrintPoly(PS);
return 0;
}
Rear初值问题
- Rear初值为NULL
- Rear指向一个空结点
/* 读入多项式 */
void Attach(int c, int e, Polynomial* pRear) {
Polynomial P;
P = (Polynomial)malloc(sizeof(struct Polynomial)); //申请空结点
P->coef = c; //对新结点赋值
P->expon = e;
P->link = NULL;
(*pRear)->link = P;
*pRear = P; /* 修改pRear值*/
}
/* 如何读入多项式*/
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;
}
/* 将两个多项式相加*/
Polynomial Add(Polynomial P1, Polynomial P2) {
t1 = P1; t2 = P2;
P = (Polynomial)malloc(sizeof(struct PolyNode)); P->link = NULL;
Rear = P;
while (t1 && t2) {
if (t1->expon == t2->expon) {
}
else if (t1->expon > t2->expon) {
}
else {
}
}
t1 = t1->link;
while (t1) {
t2 = P2; Rear = P;
while (t2) {
e = t1->expon + t2->expon;
c = t1->coef * t2->coef;
t2 = t2->link;
}
t1 = t1->link;
}
while (t2) {
Attach(t1->coef * t2->coef, t1->expon + t2->expon, &Rear);
t2 = t2->link;
}
return 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;
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 {
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;
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;
}
printf("\n");
}
/* 静态查找
* 方法1:顺序查找
*/
int SequentialSearch(StaticTable* Tb1, ElementType K) {
//查找关键字为K的数据元素
int i;
Tb1->Element[0] = K; // 建立哨兵
for (i = Tb1->Length; Tb1->Element[i] != K; i--);
return i; //查找成功返回所在单元下标;不成功返回0
}
typedef struct LNode* List;
struct LNode {
ElementType Element[MaxSize];
int Length;
};
/* 静态查找
* 方法2:二分查找(Binary Search)
* 条件: 有序 + 数组
*/
int BinarySearch(List Tb1, ElementType K) {
/* 在表Tb1中查找关键字为K的数据元素 */
int left, right, mid, NotFound = -1;
left = 1; //初始左边界
right = Tb1->Length; // 初始右边界
while (left <= right) {
mid = (left + right) / 2; //计算中间元素坐标
if (K < Tb1->Element[mid]) right = mid - 1;
else if (K > Tb1->Element[mid]) left = mid + 1;
else return mid; //查找成功,返回数据元素的下标
}
return NotFound; /* 查找不成功,返回-1*/
}
n个结点的判定树的深度为[log2(n)] + 1。
一些概念:
1、 结点的度(Degree):结点的子树个数
2、树的度: 树的所有结点中最大的度数
3、叶结点: 度为0的结点。
4、祖先结点(Ancestor)
5、子孙结点(Descendant)
6、结点的层次(Level):根结点在第一层。
儿子-兄弟表示法
二叉树,5种基本形态
特殊二叉树:
1、斜二叉树(Skewed Binary Tree)
2、完美二叉树(Perfect Binary Tree)/满二叉树(Full Binary Tree)
3、 完全二叉树(Complete Binary Tree): 允许缺失后面的结点,对应于满二叉树的中间结点不允许缺失。
二叉树的几个重要性质:
重要操作: 是否为空,遍历,创建二叉树。
- 非根结点的父结点的序号是[i/2] (取整)
- 左孩子为2i
- 右孩子为 2i+1
链表存储
typedef struct TreeNode* BinTree;
Typedef BinTree Position;
struct TreeNode {
ElementType Data;
BinTree Left;
BinTree Right;
};
/* (1)先序遍历: 根结点 左子树 右子树*/
void PreOrderTraversal(BinTree BT) {
if (BT) {
printf("%d", BT->Data);
PreOrderTraversal(BT->Left);
PreOrderTraversal(BT->Right);
}
}
/* (2)中序遍历:左子树 根结点 右子树*/
void InOrderTraversal(BinTree BT) {
if (BT) {
InOrderTraversal(BT->Left);
printf("%d", BT->Data);
InOrderTraversal(BT->Right);
}
}
/* (3)后序遍历:左子树 右子树 根结点*/
void PostOrderTraversal(BInTree BT) {
if (BT) {
PostOrderTraversal(BT->Left);
PostOrderTraversal(BT->Right);
printf("%d", BT->Data);
}
}
/* 中序遍历非递归遍历算法 */
void InOrderTraversal(BinTree BT) {
BinTree T = BT;
Stack S = CreateStack(MaxSize); //创建并初始化堆栈S
while (T || !IsEmpty(S)) {
while (T) { /* 一直向左并将沿途结点压入堆栈*/
Push(S, T);
T = T->Left;
}
if (!IsEmpty(S)) {
T = Pop(S); /* 结点弹出堆栈*/
printf("%d", T->Data); /* 访问(打印)结点*/
T = T->Right; //转向右子树
}
}
}
/* 先序遍历非递归遍历算法 */
void InOrderTraversal(BinTree BT) {
BinTree T = BT;
Stack S = CreateStack(MaxSize); //创建并初始化堆栈S
while (T || !IsEmpty(S)) {
while (T) { /* 一直向左并将沿途结点压入堆栈*/
printf("%d", T->Data); /* 访问(打印)结点*/ //第一次碰到就访问
Push(S, T);
T = T->Left;
}
if (!IsEmpty(S)) {
T = Pop(S); /* 结点弹出堆栈*/
T = T->Right; //转向右子树
}
}
}
层序遍历
/* 二叉树 层序遍历 队列*/
void LevelOrderTraversal(BinTree BT) {
Queue Q; BinTree T;
if (!BT) return; /* 若是空树,直接返回*/
Q = CreateQueue(MaxSize); /* 创建并初始化队列Q*/
AddQ(Q, BT);
while (!IsEmptyQ(Q)) {
T = DeleteQ(Q);
printf("%d\n", T->Data); /* 访问取出队列的结点*/
if (T->Left) AddQ(Q, T->Left);
if (T->Right) AddQ(Q, T->Right);
}
}
/* 输出二叉树中的叶子结点 */
// 左右子树是否都为空
void PreOrderPrintLeaves(BinTree BT) {
if (BT) {
if (!BT->Left && !BT->Right)
printf("%d", BT->Data);
PreOrderPrintLeaves(BT->Left);
PreOrderPrintLeaves(BT->Right);
}
}
/* 求二叉树的高度 */
int PostOrderGetHeight(BinTree BT) {
int HL, HR, MaxH;
if (BT) {
HL = PostOrderGetHeight(BT->Left); /* 求左子树的深度 */
HR = PostOrderGetHeight(BT->Right); /* 求右子树的深度 */
MaxH = (HL > HR) ? HL : HR;
return (MaxH + 1);
}
else return 0; //空树深度为0
}
由两种遍历序列确定二叉树
/* 二叉树表示 结构数组 静态链表*/
#define MaxTree 10
#define ElementType char
# define Tree int
#define Null -1 // 表示空
struct TreeNode {
ElementType Element;
Tree Left;
Tree Right;
} T1[MaxTree], T2[MaxTree];
int main() {
Tree R1, R2;
R1 = BuildTree(T1);
R2 = BuildTree(T2);
if (Isomorphic(R1, R2)) printf("Yes\n");
else printf("No\n");
return 0;
}
/* 建二叉树 */
Tree BuildTree(struct TreeNode T[]) {
scanf("%d\n", &N);
if (N) {
for (i = 0; i < N; i++) check[i] = 0; //一开始都是0
for (i = 0; i < N; i++) {
scanf("%c %c %c\n", &T[i].Element, &cl, &cr);
if (cl != '-') { // 有指向
T[i].Left = cl - '0';
check[T[i].Left] = 1; // 置1
}
else T[i].Left = Null;
}
for (i = 0; i < N; i++)
if (!check[i]) break; // 没有指向的,为根结点
Root = i;
}
return Root;
}
/* 判断两二叉树同构 */
int Isomorphic(Tree R1, Tree R2) {
if ((R1 == Null) && (R2 == Null))
return 1; // 两个都为空
if (((R1 == Null) && (R2 != Null)) || ((R1 != Null) && (R2 == Null)))
return 0; //其中一个为空
if (T1[R1].Element != T2[R2].Element)
return 0;
if ((T1[R1].Left == Null) && (T2[R2].Left == Null))
return Isomorphic(T1[R1].Right, T2[R2].Right);
if (((T1[R1].Left != Null) && (T2[R2].Left != Null)) &&
((T1[T1[R1].Left].Element) == (T2[T2[R2].Left].Element)))
//不需要交换左右两边
return (Isomorphic(T1[R1].Left, T2[R2].Left) &&
Isomorphic(T1[R1].Right, T2[R2].Right));
else //需要交换左右两边
return (Isomorphic(T1[R1].Left, T2[R2].Right) &&
Isomorphic(T1[R1].Right, T2[R2].Left));
}
1、左子树的所有健值小于根结点的健值。
2、右子树的所有健值大于其根结点的健值。
3、左、右子树都是二叉搜索树。
/* 二叉搜索树的查找*/
// 尾递归
Position Find(ElementType X, BinTree BST) {
if (!BST) return NULL; /* 查找失败*/
if (X > BST->Data)
return Find(X, BST->Right); //找右子树
else if (X < BST->Data)
return Find(X, BST->Left); /* 在左子树中继续查找*/
else
return BST; /* 查找成功,返回找到的结点的地址*/
}
// 改成迭代
Position IterFind(ElementType X, BinTree BST) {
while (BST) {
if (X > BST->Data)
BST = BST->Right;
else if (X < BST->Data)
BST = BST->Left;
else
return BST;
}
return NULL;
}
找最值
/* 搜索二叉树:查找最小元素的 递归 函数*/
Position FindMin(BinTree BST) {
if (!BST) return NULL; /* 空的二叉搜索树,返回NULL*/
else if (!BST->Left)
return BST; /* 找到最左叶结点并返回*/
else
return FindMin(BST->Left); /* 沿左分支继续查找*/
}
/* 搜索二叉树:查找最大元素的 迭代 函数*/
Position FindMax(BinTree BST) {
if (BST)
while (BST->Right)
BST = BST->Right; /* 沿右分支继续查找,直到最右叶结点*/
return BST;
}
/* 二叉搜索树的插入算法*/
BinTree Insert(ElementType X, BinTree BST) {
if (!BST) { /* 原树为空,生成并返回一个结点的二叉搜索树*/
BST = malloc(sizeof(struct TreeNode));
BST->Data = X;
BST->Left = BST->Right = NULL;
}
else /* 开始找要插入元素的位置*/
if (X < BST->Data)
BST->Left = Insert(X, BST->Left); /* 递归插入左子树*/
else if (X > BST->Data)
BST->Right = Insert(X, BST->Right); /* 递归插入右子树*/
return BST;
}
三种情况:
1、叶结点
2、只有一个孩子
3、双结点:
/* 二叉搜索树的 删除*/
BinTree Delete(ElementType X, BinTree BST) {
Position Tmp;
if (!BST) printf("要删除的元素未找到");
else if (X < BST->Data)
BST->Left = Delete(X, BST->Left); //左子树递归删除
else if (X > BST->Data)
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;
}
补充代码
typedef struct AVLNode* Position;
typedef Position AVLTree; /* AVL 树类型*/
struct AVLNode {
ElementType Data; /* 结点数据 */
AVLtree Left; /* 指向左子树 */
AVLTree Right; /* 指向右子树 */
int Height; /* 树高 */
};
int Max(int a, int b) {
return a > b ? a : b;
}
AVLTree SingleLeftRotation(AVLTree A) {
/* 将A与B做左单旋,更新A与B的高度,返回新的根结点B*/
AVLTree B = A->Left;
A->Left = B->Right;
B->Right = A;
A->Height = Max(GetHeight(A->Left), GetHeight(A->Right)) + 1;
B->Height = Max(GetHeight(B->Left), A->Height) + 1;
return B;
}
AVLTree DoubleLeftRightRotation(AVLTree A) {
/* 将A、B与C分别做两次单旋,返回新的根结点C*/
/* 将B与C做右单旋,C被返回*/
A->Left = SingleRightRotation(A->Left);
/* 将A与C做左单旋, C被返回*/
return SingleLeftRotation(A);
}
/* 对称的右单旋与右-左双旋 待实现*/
AVLTree Insert(AVLTree T, ElementType X) {
/* 将X插入AVL树T中,并且返回调整后的AVL树*/
if (!T) {/* 若插入空树,则新建包含一个结点的树*/
T = (AVLTree)malloc(sizeof(struct AVLTree));
T->Data = X;
T->Height = 0;
T->Right = T->Left = NULL;
}
else if (X < T->Data) {
/* 插入T的左子树*/
T->Left = Insert(T->Left, X);
/* 如果需要左旋*/
if (GetHeight(T->Left) - GetHeight(T->Right) == 2)
if (X < T->Left->Data)
T = SingleLeftRotation(T); //左单旋
else
T = DoubleLeftRightRotation(T); /* 左-右双旋*/
}
else if (X > T->Data) {//插入T的右子树
T->Right = Insert(T->Right, X);
/* 如果需要右旋*/
if (GetHeight(T->Left) - GetHeight(T->Right) == -2)
if (X > T->Right->Data)
T = SingleRightRotation(T); /* 右单旋*/
else
T = DoubleRightLeftRotation(T); /* 右-左双旋*/
}
/* else X == T-> Data, 无需插入*/
/* 更新树高 */
T->Height = Max( GetHeight(T->Left), GetHeight(T->Right)) + 1;
return T;
}
/* */
/* 1、搜索树表示*/
typedef struct TreeNode* Tree;
struct TreeNode {
int v;
Tree Left, Right;
int flag; // 标记是否被访问过
};
/* 2、 建搜索树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;
}
Tree MakeTree(int N) {
Tree T;
int i, V;
scanf("%d", &V);
T = NewNode(V);
for (i = 1; i < N; i++) {
scanf("%d", &V);
T = Insert(T, V);
}
return T;
}
/* 3、判别一序列是否与搜索树T一致 */
// 在树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;
}
}
int Judge(Tree T, int N) {
int i, V, flag = 0; /* flag: 0代表目前还一致,1代表已经不一致*/
scanf("%d", &V);
if (V != T->v) flag = 1;
else T->flag = 1;
for (i = 1; i < N; i++) {
scanf("%d", &V);
if ((!flag) && (!check(T, V))) flag = 1;
}
if (flag) return 0;
else return 1;
}
void ResetT(Tree T) {/* 清除T中各结点的flag标记*/
if (T->Left) ResetT(T->Left);
if (T->Right) ResetT(T->Right);
T->flag = 0;
}
void FreeTree(Tree T) {/* 释放T的空间*/
if (T->Left) FreeTree(T->Left);
if (T->Right) FreeTree(T->Right);
free(T);
}
int main() {
int N, L, i;
Tree T;
scanf("%d", &N);
while (N) {
scanf("%d", &L);
T = MakeTree(N);
for (i = 0; i < L; i++) {
if (Judge(T, N)) printf("Yes\n");
else printf("No\n");
ResetT(T); /* 清除T中的标记flag*/
}
FreeTree(T);
scanf("%d", &N);
}
return 0;
}
堆:
1、结构性: 用数组表示的完全二叉树
2、有序性:最大堆,最小堆。
- 从根结点到任意结点路径上结点序列 有序。
主要操作:创建一个空堆,是否已满, 插入,是否为空, 返回最大。
/* 创建最大堆 */
typedef struct HeapStruct* MaxHeap;
struct HeapStruct {
ElementType* Elements; /* 存储堆元素的数组*/
int size; /* 堆的当前元素个数*/
int Capacity; /* 堆的最大容量 */
};
MaxHeap Create(int MaxSize) {
MaxHeap H = malloc(sizeof(struct HeapStruct));
H->Elements = malloc((MaxSize + 1) * sizeof(ElementType)); //数组空间
H->size = 0;
H->Capacity = MaxSize;
H->Elements[0] = MaxData; /* 定义“哨兵"为大于堆中所有可能元素的值,便于以后更快操作*/
return H;
}
/* 堆操作: 插入*/
void Insert(MaxHeap H, ElementType item) {
/* 将元素item 插入到最大堆H,其中H-> Elements[0] 已经定义为哨兵*/
int i;
if (IsFull(H)) {
printf("最大堆已满");
return;
}
i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置*/
for (; H->Elements[i / 2] < item; i /= 2)
H->Elements[i] = H->Elements[i / 2]; /* 向下过滤结点*/
H->Elements[i] = item; /* 将item插入*/
}
/* 堆操作: 删除元素*/
ElementType DeleteMax(MaxHeap H) {
/* 从最大堆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) {
Child = Parent * 2;
if ((Child != H->Size) &&
(H->Elements[Child] < H->Elements[Child + 1]))
Child++; /* Child指向左右子结点的较大者*/
if (temp >= H->Elements[Child]) break;
else /* 移动temp元素到下一层*/
H->Elements[Parent] = H->Elements[Child];
}
H->Elements[Parent] = temp;
return MaxItem;
}
其他:
typedef struct HNode *Heap; /* 堆的类型定义 */
struct HNode {
ElementType *Data; /* 存储元素的数组 */
int Size; /* 堆中当前元素个数 */
int Capacity; /* 堆的最大容量 */
};
typedef Heap MaxHeap; /* 最大堆 */
typedef Heap MinHeap; /* 最小堆 */
#define MAXDATA 1000 /* 该值应根据具体情况定义为大于堆中所有可能元素的值 */
MaxHeap CreateHeap( int MaxSize )
{ /* 创建容量为MaxSize的空的最大堆 */
MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
H->Data = (ElementType *)malloc((MaxSize+1)*sizeof(ElementType));
H->Size = 0;
H->Capacity = MaxSize;
H->Data[0] = MAXDATA; /* 定义"哨兵"为大于堆中所有可能元素的值*/
return H;
}
bool IsFull( MaxHeap H )
{
return (H->Size == H->Capacity);
}
bool Insert( MaxHeap H, ElementType X )
{ /* 将元素X插入最大堆H,其中H->Data[0]已经定义为哨兵 */
int i;
if ( IsFull(H) ) {
printf("最大堆已满");
return false;
}
i = ++H->Size; /* i指向插入后堆中的最后一个元素的位置 */
for ( ; H->Data[i/2] < X; i/=2 )
H->Data[i] = H->Data[i/2]; /* 上滤X */
H->Data[i] = X; /* 将X插入 */
return true;
}
#define ERROR -1 /* 错误标识应根据具体情况定义为堆中不可能出现的元素值 */
bool IsEmpty( MaxHeap H )
{
return (H->Size == 0);
}
ElementType DeleteMax( MaxHeap H )
{ /* 从最大堆H中取出键值为最大的元素,并删除一个结点 */
int Parent, Child;
ElementType MaxItem, X;
if ( IsEmpty(H) ) {
printf("最大堆已为空");
return ERROR;
}
MaxItem = H->Data[1]; /* 取出根结点存放的最大值 */
/* 用最大堆中最后一个元素从根结点开始向上过滤下层结点 */
X = H->Data[H->Size--]; /* 注意当前堆的规模要减小 */
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];
}
H->Data[Parent] = X;
return MaxItem;
}
/*----------- 建造最大堆 -----------*/
void PercDown( MaxHeap H, int p )
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
int Parent, Child;
ElementType 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++; /* Child指向左右子结点的较大者 */
if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
else /* 下滤X */
H->Data[Parent] = H->Data[Child];
}
H->Data[Parent] = X;
}
void BuildHeap( MaxHeap H )
{ /* 调整H->Data[]中的元素,使满足最大堆的有序性 */
/* 这里假设所有H->Size个元素已经存在H->Data[]中 */
int i;
/* 从最后一个结点的父节点开始,到根结点1 */
for( i = H->Size/2; i>0; i-- )
PercDown( H, i );
}
/* 附录代码 堆*/
typedef struct HNode* Heap; /* 堆的类型定义*/
struct HNode {
ElementType* Data; /* 存储元素的数组*/
int size; /* 堆中当前元素个数*/
int Capacity; /* 堆的最大容量 */
};
typedef Heap MaxHeap; /* 最大堆 */
typedef Heap MinHeap; /* 最小堆*/
#define MAXDATA 1000 /* 大于堆中所有可能元素的值*/
MaxHeap CreateHeap(int MaxSize) {
/* 创建容量为MaxSize的空的最大堆*/
MaxHeap H = (MaxHeap)malloc(sizeof(struct HNode));
H->Data = (ElementType*)malloc(MaxSize + 1) * sizeof(ElementType);
H->Size = 0;
H->Capacity = MaxSize;
H->Data[0] = MAXDATA; /* 定义 哨兵 为大于堆中所有可能元素的值*/
return H;
}
bool IsFull(MaxHeap H) {
return (H->Size == H->Capacity);
}
bool Insert(MaxHeap H, ElementType X) {
/* 将元素X插入最大堆H, 其中H->Data[0] 已经定义为 哨兵*/
int i;
if (IsFull(H)) {
printf("最大堆已满");
return false;
}
i = ++H->Size; /* i指向插入后 堆中的最后一个元素的位置*/
for (; H->Data[i / 2] < X; i /= 2)
H->Data[i] = H->Data[i / 2]; /* 上滤X*/
H->Data[i] = X; /* 将X插入*/
return true;
}
#define ERROR -1 /* 定义为堆中不可能出现的元素值*/
bool IsEmpty(MaxHeap H) {
return (H->size == 0);
}
ElementType DeleteMax(MaxHeap H) {
/* 从最大堆中取出健值为最大的元素,并删除一个结点*/
int Parent, Child;
ElementType MaxItem, X;
if (IsEmpty(H)) {
printf("最大堆已为空");
return ERROR;
}
MaxItem = H->Data[1]; /* 取出根节点存放的最大值*/
/* 用最大堆中最后一个元素从根结点开始向上过滤下层结点*/
X = H->Data[H->size--];
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
H->Data[Parent] = H->Data[Child];
}
H->Data[Parent] = X;
return MaxItem;
}
/* 创建最大堆 */
void PercDown(MaxHeap H, int p) {
/* 下滤: 将H->Data[p]为根的子堆调整为最大堆*/
int Parent, Child;
ElementType 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(MaxHeap H) {
/* 调整H->Data[]中元素,使满足最大堆的有序性*/
/* 这里假设所有H->Size个元素已经存在H-> Data[]中*/
int i;
/* 从最后一个结点的父结点开始,到根结点1*/
for (i = H->Size / 2; i > 0; i--)
PercDown(H, i);
}
/* 哈夫曼树创建 */
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); /* 将H-> Elements[]按权值调整为最小堆*/
for (i = 1; i < H->Size; i++) {/* 做H->Size-1次合并*/
T = malloc(sizeof(struct TreeNode)); /* 建立新结点*/
T->Left = DeleteMin(H); /* 从最小堆中删除一个结点,作为新T的左子结点*/
T->Right = DeleteMin(H); /* 从最小堆中删除一个结点,作为新T的右子结点*/
T->Weight = T->Left->Weight + T->Right->Weight; /* 计算新权值*/
Insert(H, T); /* 将新T插入最小堆*/
}
T = DeleteMin(H);
return T;
}
(1)左右分支:0、1
(2)字符只在叶结点上
typedef struct {
ElementType Data;
int Parent;
}SetType;
/* 集合运算: 查找某个元素所在的集合(用根结点表示)*/
int Find(SetType S[], ElementType X) {
/* 在数组S中查找值为X的元素所属的集合*/
/* MaxSize是全局变量,为数组S的最大长度*/
int i;
for (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;
}
/* 集合的并运算*/
/*1、分别找到X1和X2两个元素所在集合树的根节点
* 2、如果不同根,则将其中一个根结点的父结点指针设置成另一个根结点的数组下标
*/
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;
}
/* 为了改善合并以后的查找性能,应将小的集合合并到大的集合中*/
/* 堆的表示及其操作*/
#define MAXN 1001
#define MINH -10001
int H[MAXN], size;
void Create() {
size = 0;
H[0] = MINH; /* 设置 岗哨*/
}
void Insert(int X) {
/* 将X插入H, 省略检查堆是否已满的代码*/
int i;
for (i = ++size; H[i / 2] > X; i /= 2)
H[i] = H[i / 2];
H[i] = X;
}
int main() {
int n, m, x, i, j;
scanf("%d %d", &n, &m);
Create(); /* 堆初始化*/
for (i = 0; i < n; i++) {/* 以逐个插入的方式建堆*/
scanf("%d", &x);
Insert(x);
}
//查询
for (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;
}
结合的表示(常规)
/* 集合的表示(常规)*/
typedef struct {
ElementType Data;
int Parent;
}SetType;
int Find(SetType S[], ElementType X) {
int i;
for (i = 0; i < MaxSize && S[i].Data != X; i++);
if (i >= MaxSize) return -1;
for (; S[i].Parent >= 0; i = S[i].Parent);
retutn i;
}
集合的简化表示
/* 集合的简化表示*/
typedef int ElementType; /* 默认元素可以用非负整数表示*/
typedef int SetName; /* 默认用根结点的下标作为集合名称*/
typedef ElementType SetType[MaxSize];
SetName Find(SetType S, ElementType X) {
/* 默认集合元素全部初始化为-1*/
for (; S[X] >= 0; X = S[X]);
return X;
}
void Union(SetType S, SetName Root1, SetName Root2) {
/* 默认Root1和Root2是不同集合的根结点*/
S[Root2] = Root1;
}
/* 程序框架*/
int main() {
SetType S;
int n;
char in;
scanf("%d\n", &n);
Initialization(S, n);
do {
scanf("%c", &in);
switch (in) {
case 'I': Input_connection(S); break; // Union(Find)
case 'C': Check_connection(S); break; //Find
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 (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);
}
改进1:按秩归并
/* 按秩归并: 比树高*/
if (S[Root2] < S[Root1])
S[Root1] = Root2;
else {
if (S[Root1] == S[Root2]) S[Root1]--; //值为负的,要增大,故为--
S[Root2] = Root1;
}
/* 按秩归并: 比规模(把小树贴到大树上)*/
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;
}
}
改进2:路径压缩
/* 改进2: 路径压缩*/
SetName Find(SetType S, ElementType X) {
if (S[X] < 0) /* 找到集合的根*/
return X;
else
return S[X] = Find(S, S[X]); //伪递归
/*先找到根;把根变成X的父结点;再返回根。 */
}
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);
}
完全二叉树: 编号不缺失
二叉搜索树: 左<根<右
使用数组的原因:
1、完全二叉树,不浪费空间
2、层序遍历==直接顺序输出
先序遍历
void solve(int ALeft, int ARight, int TRoot)
{ //A的左端,右端, 结果数组
/* 初始调用为 solve(0, N-1, 0) */
n = ARight - ALeft + 1; //序列长度
if (n == 0) return;
L = GetLeftLength(n); /* 计算n个结点的树其左子树有多少个结点*/
T[TRoot] = A[ALeft + L];
LeftTRoot = TRoot * 2 + 1; //下标从0开始
RightTRoot = LeftTRoot + 1;
solve(ALeft, ALeft + L - 1, LeftTRoot);
solve(ALeft + L + 1, ARight, RightTRoot);
}
/* 排序部分*/
int compare(const void* a, const void* b)
{
return *(int*)a - *(int*)b;
}
#include
int main() {
...
qsort(A, N, sizeof(int), compare);
}
1、最优编码:总长度(WPL)最小
2、无歧义编码: 前缀码(数据仅存于叶子结点)
3、没有度为1的结点(满足1、2则必然有3)
/* 1、计算最优编码长度*/
MinHeap H = CreateHeap(N); /* 创建一个空的,容量为N的最小堆*/
H = ReadData(N); /* 将f[] 读入H->Data[]中*/
HuffmanTree T = Huffman(H); /* 建立Huffman树*/
int CodeLen = WPL(T, 0);
int WPL(HuffmanTree T, int Depth) {
if (!T->Left && !T->Right)
return (Depth * T->Weight);
else
return (WPL(T->Left, Depth + 1)
+ WPL(T->Right, Depth + 1));
}
/*2、 检查: 长度是否正确; 是否满足前缀码要求*/
/* 单链表的逆转*/
Ptr Reverse(Ptr head, int K) {
new = head->next;
old = new->next;
while (cnt <K) {
tmp = old->next;
old->next = new;
new = old; old = tmp;
cnt++;
}
head->next->next = old;
return new;
}
- 1、对角线值为0:不允许自回路
- 2、对称
适用于稠密图
/* 图的邻接矩阵表示*/
#define MaxVertexNum 100 /* 最大顶点数设为100*/
#define INFINITY 65535 /* ∞设为双字节无符号整数的最大值65535*/
typedef int Vertex; /* 用顶点下标表示顶点,为整型*/
typedef int WeightType; /* 边的权值设为整型*/
typedef char DataType; /* 顶点存储的数据类型设为字符型 */
/* 边的定义 */
typedef struct ENode* PtrToENode;
struct ENode {
Vertex V1, V2; /* 有向边*/
WeightType Weight; /* 权重*/
};
typedef PtrToENode Edge;
/* 图结点的定义*/
typedef struct GNode* PtrToGNode; //指向该结点的指针
struct GNode {
int Nv; /* 顶点数*/
int Ne; /* 边数*/
WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵*/
DataType Data[MaxVertexNum]; /* 存顶点的数据*/
/* 很多情况下,顶点无数据,此时Data[]可以不用出现*/
};
typedef PtrToGNode MGraph; /* 以邻接矩阵存储的图类型*/
MGraph CreateGraph(int VertexNum) {
/* 初始化一个有VertexNum个顶点但没有边的图*/
Vertex V, W; // 顶点,实际是整型
MGraph Graph;
Graph = (MGraph)malloc(sizeof(struct GNode)); /* 建立图*/
Graph->Nv = VertexNum;
Graph->Ne = 0;
/* 初始化邻接矩阵:这里默认顶点编号从0开始,到(Graph->Nv-1)*/
for (V = 0; V < Graph->Nv; V++)
for (W = 0; W < Graph->Nv; W++)
Graph->G[V][W] = INFINITY; /* 或0 */
return Graph;
}
void InsertEdge(MGraph Graph, Edge E) {
/* 插入边*/
Graph->G[E->V1][E->V2] = E->Weight;
/* 若是无向图,还要插入边*/
Graph->G[E->V2][E->V1] = E->Weight;
}
MGraph BuildGraph() {
MGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf("%d", &Nv); /* 读入顶点个数*/
Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图*/
scanf("%d", &(Graph->Ne)); /* 读入边数*/
if (Graph->Ne != 0) {/* 如果有边*/
E = (Edge)malloc(sizeof(struct ENode)); /* 建立边结点*/
/* 读入边, 格式为 起点,终点, 权重, 插入邻接矩阵*/
for (i = 0; i < Graph->Ne; i++) {
scanf("%d %d %d", &E->V1, &E->V2, &E->Weight);
/* 注意: 如果权重不是整型,Weight的读入格式要改*/
InsertEdge(Graph, E);
}
}
/* 如果顶点有数据,读入数据*/
for (V = 0; V < Graph->Nv; V++)
scanf(" %c", &(Graph->Data[V]));
return Graph;
}
/* 图的邻接表 表示*/
#define MaxVertexNum 100 /* 最大顶点数设为100*/
typedef int Vertex; /* 用顶点下标表示顶点,为整型*/
typedef int WeightType; /* 边的权值设为整型*/
typedef char DataType; /* 顶点存储的数据类型设为字符型 */
/* 边的定义 */
typedef struct ENode* PtrToENode;
struct ENode {
Vertex V1, V2; /* 有向边*/
WeightType Weight; /* 权重*/
};
typedef PtrToENode Edge;
/* 邻接点的定义 */
typedef struct AdjVNode* PtrToAdjVNode;
struct AdjVNode {
Vertex AdjV; /* 邻接点下标*/
WeightType Weight; /* 边权重*/
PtrToAdjVNode Next; /* 指向下一个邻接点的指针*/
};
/* 顶点表头 结点的定义*/
typedef struct Vnode {
PtrToAdjVNode FirstEdge; /* 边表头指针*/
DataType Data; /* 存顶点的数据*/
/* 很多情况下,顶点无数据,此时Data[]可以不用出现*/
}AdjList[MaxVertexNum]; /* AdjList是邻接表类型*/
/* 图结点的定义*/
typedef struct GNode *PtrToGNode;
struct GNode {
int Nv; /* 顶点数*/
int Ne; /* 边数 */
AdjList G; /* 邻接表*/
};
typedef PtrToGNode LGraph; /* 以邻接表方式存储的图类型*/
LGraph CreateGraph(int VertexNum) {
/* 初始化一个有VertexNum个顶点但没有边的图*/
Vertex V;
LGraph Graph;
Graph = (LGraph)malloc(sizeof(struct GNode)); /* 建立图*/
Graph->Nv = VertexNum;
Graph->Ne = 0;
/* 初始化邻接表头指针:这里默认顶点编号从0开始,到(Graph->Nv-1)*/
for (V = 0; V < Graph->Nv; V++)
Graph->G[V].FirstEdge = 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;
/* 若是无向图,还要插入边*/
/* 为 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;
}
LGraph BuildGraph() {
LGraph Graph;
Edge E;
Vertex V;
int Nv, i;
scanf("%d", &Nv); /* 读入顶点个数*/
Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图*/
scanf("%d", &(Graph->Ne)); /* 读入边数*/
if (Graph->Ne != 0) {/* 如果有边*/
E = (Edge)malloc(sizeof(struct ENode)); /* 建立边结点*/
/* 读入边, 格式为 起点,终点, 权重, 插入邻接表*/
for (i = 0; i < Graph->Ne; i++) {
scanf("%d %d %d", &E->V1, &E->V2, &E->Weight);
/* 注意: 如果权重不是整型,Weight的读入格式要改*/
InsertEdge(Graph, E);
}
}
/* 如果顶点有数据,读入数据*/
for (V = 0; V < Graph->Nv; V++)
scanf(" %c", &(Graph->G[V].Data));
return Graph;
}
强联通
弱联通
/* 邻接表存储的图 _ DFS */
void Visit(Vertex V) {
printf("正在访问顶点%d\n", V);
}
/* Visit[] 为全局变量,已经初始化为false */
void DFS(LGraph Graph, Vertex V, void (*Visit)(Vertex)) {
/* 以V为出发点对邻接表存储的图Graph进行DFS搜索*/
PtrToAdjVNode W;
Visit(V); /* 访问第V个顶点*/
Visited[V] = true; /* 标记V已访问*/
for (W = Graph->G[V].FirstEdge; W = W->Next)/* 对V的每个邻接点W->AdjV*/
if (!Visited[W->AdjV]) /* 若W-> AdjV未被访问*/
DFS(Graph, W->AdjV, Visit); /* 递归访问 */
}
/* 邻接矩阵存储的图 _ BPS */
/* IsEdge(Graph, V, W) 检查是否图Graph中的一条边,即W是否V的邻接点*/
/* 此函数根据图的不同类型要做不同的实现,关键取决于对不同边的表示方法*/
/* 例如对有权图,如果不存在的边被初始化为INFINITY, 则函数实现如下:*/
bool IsEdge(MGraph Graph, Vertex V, Vertex W) {
return Graph->G[V][W]<INFINITY ? true : false>;
}
/* Visited[] 为全局变量,已经初始化为false*/
void BFS(MGraph Graph, Vertex S, void (*Visit)(Vertex)) {
/* 以S为出发点对邻接矩阵存储的图Graph进行BFS搜索*/
Queue Q;
Vertex V, W;
Q = CreateQueue(MaxSize); /* 创建空队列,MaxSize为外部定义的常数*/
/* 访问顶点S: 此处可根据具体访问需要改写 */
Visit(S);
Visited[S] = true; /* 标记S已访问 */
AddQ(Q, S); /* S入队列*/
while (!IsEmpty(Q)) {
V = DeleteQ(Q); /* 弹出V */
for(W=0; W< Graph -> Nv; W++) /* 对图中的每个顶点W */
/* 若W是V的邻接点并且未访问过 */
if (!Visited[W] && IsEdge(Graph, V, W)) {
/* 访问顶点 */
Visit(W);
Visited[W] = true; /* 标记W已访问*/
AddQ(Q, W); /* W入队列 */
}
}
}
/* 总体算法 */
void ListComponents(Graph G) {
for(each V in G)
if (!visited[V]) {
DFS(V);
}
}
void Save007(Graph G) {
for (each V in G) {
if (!visitedd[V] && FirstJump(V)) {
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(each W in G)
if (!visited[W] && Jump(V, W)) {
answer = DFS(W);
if (answer == YES) break;
}
}
return answer;
}
/* 六度空间 */
void SDS() {
for (each V in G) {
count = BFS(V);
output(count / N);
}
}
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;
}
/* 建图: 邻接矩阵 */
int G[MAXN][MAXN], Nv, Ne;
void BuildGraph() {
int i, j, v1, v2, w;
scanf("%d", &Nv);
/* CreateGraph*/
for (i = 0; i < Nv; i++)
for (j = 0; j < Nv; j++)
G[i][j] = 0; /* 或 INIFINITY*/
scanf("%d", &Ne);
for (i = 0; i < Ne; i++) {
scanf("%d %d %d", &v1, &v2, &w);
/* InsertEdge*/
G[v1][v2] = w;
G[v2][v1] = w;
}
}
- 最短路径(Shortest Path)
- 源点(Source)
- 终点(Destination)
/* 无权图的单源最短路算法*/
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);
}
}
}
// T = O(|V| + |E|)
/* 邻链表存储 - 无权图的单源最短路算法*/
/* dist[] 和 path[] 全部初始化为-1 */
void Unweighted(LGraph Graph, int dist[], int path[], Vertex S) {
Queue Q;
Vertex V;
PtrToAdjVNode W;
Q = CreateQueue(Graph->Nv); /* 创建空队列, MaxSize为外部定义的常数*/
dist[S] = 0; /* 初始化源点*/
AddQ(Q, S);
while (!IsEmpty(Q)) {
V = DeleteQ(Q);
for(W = Graph-> G[V].FirstEdge; W; W = W-> W->Next)/* 对V的每个邻接点W->AdjV*/
if (dist[W->AdjV] == -1) {//若W->AdjV未被访问过
dist[W->AdjV] = dist[V] + 1; /* W->AdjV 到S的距离更新*/
path[W->AdjV] = V; /* 将V记录在S到W->AdjV 的路径上*/
AddQ(Q, W->AdjV);
}
}
}
/* 有权图的单源最短路算法 */
void Dijkstra(Vertex S) {
while (1) {
V = 未收录顶点中的dist最小者;
if (这样的V不存在)
break;
collected[V] = true;
for(V的每个邻接点 W)
if(collected[W] == false)
if (dist[V] + E<v, w> < dist[W]) {
dist[W] = dist[V] + E <V, W>;
path[W] = V;
}
}
}
/*邻接矩阵存储 - 有权图的单源最短路算法 */
Vertex FindMinDist(MGraph Graph, int dist[], int collected[]) {
/* 返回未被收录顶点中dist最小者*/
Vertex MinV, V;
int MinDist = INFINITY;
for (V = 0; V < Graph->Nv; V++) {
if (collected[V] == false && dist[V] < MinDist) {
/* 若V未被收录, 且dist[V]更小*/
MinDist = dist[V]; /* 更新最小距离*/
MinV = V; /* 更新对应顶点*/
}
}
if (MinDist < INFINITY) /* 若找到最小dist*/
return MinV; /* 返回对应顶点下标*/
else return ERROR; /* 若这样的顶点不存在,返回错误标记*/
}
bool Dijkstra(MGraph Graph, int dist[], int path[], Vertex S) {
int collected[MaxVertexNum];
Vertex V, W;
/* 初始化: 此处默认邻接矩阵中不存在的边用INFINITY表示*/
for (V = 0; V < Graph->Nv; V++) {
dist[V] = Graph->G[S][V];
if (dist[V] < INFINITY)
path[V] = S;
else
path[V] = -1;
collected[V] = false;
}
/* 先将起点收入集合*/
dist[S] = 0;
collected[S] = true;
while (1) {
/* V = 未被收录顶点中的dist最小者*/
V = FindMinDist(Graph, dist, collected);
if (V == ERROR) /*这样的V不存在*/
break;
collected[V] = true; /* 收录V*/
for(W=0; W< Graph->Nv; W++) /* 对图中的每个顶点W */
/* 若W是V的邻接点并且未被收录*/
if (collected[W] == false && Graph->G[V][W] < INFINITY) {
if (Graph->G[V][W] < 0) /* 若有负边*/
return false; /* 不能正确解决, 返回错误标记*/
/* 若收录V使得dist[W]变小 */
if (dist[V] + Graph-> G[V][W] < dist[W]) {
dist[W] = dist[V] + Graph-> G[V][W]; /* 更新dist[W]*/
path[W] = V; /* 更新S到W的路径*/
}
}
}
return true;
}
/* 多源最短路径算法*/
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;
}
}
// T = O(|V|^3)
/* 邻接矩阵存储 - 多源最短路径算法*/
bool Floyd(MGraph Graph, WeightType D[][MaxVertexNum], Vertex path[][MaxVertexNum]) {
Vertex i, j, k;
/* 初始化*/
for(i=0;i<Graph-> Nv;i++)
for (j = 0; j < Graph-> Nv; j++) {
D[i][j] = Graph->G[i][j];
path[i][j] = -1;
}
for(k=0;k<Graph->Nv;k++)
for(i=0;i<Graph-> Nv;i++)
for(j=0;j<Graph->Nv;j++)
if (D[i][k] + D[k][j] < D[i][j]) {
D[i][j] = D[i][k] + D[k][j];
if (i == j && D[i][j] < 0) /* 若发现负值圈*/
return false;
path[i][j] = k;
}
return true;
}
/* 选择动物 */
void FindAnimal(MGraph Graph) {
WeightType D[MaxVertexNum][MaxVertexNum], MaxDist, MinDist;
Vertex Animal, i;
Floyd(Graph, D);
MinDist = INFINITY;
for (i = 0; i < Graph->Nv; i++) {
MaxDist = FindMaxDist(D, i, Graph->Nv);
if (MaxDist == INFINITY) {// 说明有从i无法变出的动物
printf("0\n");
return;
}
if (MinDist > MaxDist) {//找到最长距离更小的动物
MinDist = MaxDist; Animal = i + 1; /* 更新距离,记录编号*/
}
}
printf("%d %d\n", Animal, MinDist);
}
weightType FindMaxDist(weightType D[][MaxVertexNum], Vertex i, int N) {
weightType MaxDist;
Vertex j;
MaxDist = 0;
for (j = 0; j < N; j++) {// 找到i到其他动物j的最长距离
if (i != j && D[i][j] > MaxDist) {
MaxDist = D[i][j];
}
}
return MaxDist;
}
#define MaxVertexNum 100 /* 最大顶点数设为100*/
#define INFINITY 655535 /* ∞设为双字节无符号整数的最大值65535*/
typedef int Vertex; /* 用顶点下标表示顶点,为整型*/
typedef int WeightType; /* 边的权值设为整型*/
/* 边的定义*/
typedef struct ENode* PtrToENode;
struct ENode {
Vertex V1, V2; /* 有向边*/
WeightType weight; /* 权重*/
};
typedef PtrToENode Edge;
/* 图结点的定义 */
typedef struct GNode* PtrToGNode;
struct GNode {
int Nv; /* 顶点数*/
int Ne; /* 边数*/
WeightType G[MaxVertexNum][MaxVertexNum]; /* 邻接矩阵*/
};
typedef PtrToGNode MGraph; // 以邻接矩阵存储的图类型
MGRaph CreateGraph(int VertexNum) {
/* 初始化一个有VertexNum个顶点但没有边的图*/
Vertex V, W;
MGraph Graph;
Graph = (MGraph)malloc(sizeof(struct GNode)); /* 建立图*/
Graph->Nv = VertexNum;
Graph->Ne = 0;
/* 初始化邻接矩阵*/
/* 这里默认顶点编号从0开始,到 (Graph->Nv - 1 )*/
for (V = 0; V < Graph->Nv; V++) {
for (W = 0; W < Graph->Nv; W++) {
Graph->G[V][W] = INFINITY;
}
}
return Graph;
}
void InsertEdge(MGraph Graph, Edge E) {
/* 插入边 */
Graph->G[E->V1][E->V2] = E->weight;
/* 若是无向图,还要插入边*/
Graph->G[E->V2][E->V1] = E->weight;
}
MGraph BuildGraph() {
MGraph Graph;
Edge E;
int Nv, i;
scanf("%d", &Nv); /* 读入顶点个数*/
Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */
scanf("%d", &(Graph->Ne)); /* 读入边数*/
if (Graph->Ne != 0) {//如果有边
E = (Edge)malloc(sizeof(struct ENode)); /* 建立边结点*/
/* 读入边, 格式为"起点 终点 权重", 插入邻接矩阵 */
for(i=0; i< Graph->Ne;i++){
scanf("%d %d %d", &E->V1, &E->V2, &E->weight);
E->V1--; E->V2--; /* 起始编号从0开始*/
InsertEdge(Graph, E);
}
}
return Graph;
}
void Floyd(MGraph Graph, WeightType D[][MaxVertexNum]) {
Vertex i, j, k;
/* 初始化 */
for (i = 0; i < Graph->Nv; i++)
for (j = 0; j < Graph->Nv; j++) {
D[i][j] = Graph->G[i][j];
}
for (k = 0; k < Graph->Nv; k++)
for(i=0;i<Graph->Nv; i++)
for (j = 0; j < Graph->Nv; j++) {
D[i][j] = D[i][k] + D[k][j];
}
}
贪心算法
/* Prim算法: 让一棵小树长大 */
/* 顶点到已生成树的最小距离 */
/* 适用于 稠密图 T = O(|V| ^2)*/
/* 邻接矩阵存储 - Prim 最小生成树法 */
Vertex FindMinDist(MGraph Graph, WeightType dist[]) {
/* 返回未被收录顶点中dist最小者 */
Vertex MinV, V;
WeightType MInDist = INFINITY;
for (V = 0; V < Graph->Nv; V++){
if (dist[V] != 0 && dist[V] < MinDist) {
/* 若 V未被收录,且dist[V] 更小*/
MinDist = dist[V]; /* 更新最小距离*/
MinV = V; /* 更新对应顶点*/
}
}
if (MinDist < INFINITY) /* 若找到最小dist*/
return MinV; /* 返回赌赢的顶点下标*/
else return ERROR; /* 若这样的顶点不存在,返回-1作为标记*/
}
int Prim(MGraph Graph, LGraph MST) {
/* 将最小生成树保存为邻接表存储的图MST, 返回最小权重和*/
WeightType dist[MaxVertexNum], TotalWeight;
Vertex parent[MaxVertexNum], V, W;
int VCount;
Edge E;
/* 初始化, 默认初始点下标是0*/
for (V = 0; V < Graph->Nv; V++) {
/* 这里假设若V到W没有直接的边, 则Graph-> G[V][W]定义为INFINITY*/
dist[V] = Graph->G[0][V];
parent[V] = 0; /* 暂且定义所有顶点的父结点都是初始点0*/
}
TotalWeight = 0; /* 初始化权重和 */
VCount = 0; /* 初始化收录的顶点数*/
/* 创建包含所有顶点但没有边的图, 用邻接表版本*/
MST = CreateGraph(Graph->Nv);
E = (Edge)malloc(sizeof(struct ENode)); /* 建立空的边结点*/
/* 将初始点0收录进MST*/
dist[0] = 0;
VCount++;
parent[0] = -1; /* 当前树根是 0*/
while (1) {
V = FindMinDist(Graph, dist);
/* V= 未被收录顶点中dist最小者*/
if (V == ERROR) /* 若这样的点不存在*/
break; /* 算法结束 */
/* 将V及相应的边收录进MST*/
E->V1 = parent[V];
E->V2 = V;
E->Weight = dist[V];
InsertEdge(MST, E);
TotalWeight += dist[V];
dist[V] = 0;
VCount++;
for (W = 0; W < Graph->Nv; W++) {/* 对图中的每个顶点*/
if (dist[W] != 0 && Graph->G[V][W] < INFINITY) {
/* 若W是V的邻接点并且未被收录 */
if (Graph->G[V][W] < dist[W]) {
/* 若收录V使dist[]变小*/
dist[W] = Graph->G[V][W]; /* 更新 dist[W] */
parent[W] = V; /* 更新树*/
}
}
}
}
if (VCount < Graph->Nv) /* MST中收录的顶点不到|V|个*/
TotalWeight = ERROR;
return TotalWeight; /* 算法执行完毕, 返回最小权重和 或 错误标记*/
}
/* KrusKal算法: 将森林合并成树*/
/* 邻接表存储 - Kruskal最小生成树算法 */
/* ------------ 顶点并查集定义 ------------ */
typedef Vertex ElementType; /* 默认元素可以用非负数表示*/
typedef Vertex SetName; /* 默认用根结点的下标作为集合名称 */
typedef ElementType SetType[MaxVertexNum]; /* 假设集合元素下标从0开始*/
void InitializeVSet(SetType S, int N) {
/* 初始化并查集 */
ElementType X;
for (X = 0; X < N; X++) S[X] = -1;
}
void Union(SetType S, SetName Root1, SetName Root2) {
/* 这里默认Root1和Root2是不同集合的根结点*/
/* 保证小集合并入大集合*/
if (S[Root2] < S[Root1]) {/* 如果集合2比较大 */
S[Root2] += S[Root1]; /* 集合1并入集合2*/
S[Root1] = Root2;
}
else { /* 如果集合1比较大 */
S[Root1] += S[Root2]; /* 集合2并入集合1*/
S[Root2] = Root1;
}
}
SetName Find(SetType S, ElementType X) {
/* 默认集合元素全部初始化为-1*/
if (S[X] < 0) /* 找到集合的根*/
return X;
else
return S[X] = Find(S, S[X]); /* 路径压缩*/
}
bool CheckCycle(SetType VSet, Vertex V1, Vertex V2) {
/* 检查连接V1和V2的边是否在现有的最小生成子树中构成回路*/
Vertex Root1, Root2;
Root1 = Find(VSet, V1); /* 得到V1所属的连通集名称*/
Root2 = Find(VSet, V2); /* 得到V2所属的连通集名称*/
if (Root1 == Root2) /* 若V1 和V2已经连通, 则该边不能要*/
return false;
else {/* 否则该边可以被收集, 同时将V1和V2并入同一连通集*/
Union(VSet, Root1, Root2);
return true;
}
}
/* ------------------ 边的最小堆定义 ---------------------------*/
void PercDown(Edge ESet, int p, int N) {
/* 将N个元素的边数组中ESet[p]为根的子堆调整为关于Weight的最小堆 */
int Parent, Child;
struct ENode X;
X = ESet[p]; /* 取出根结点存放的值 */
for (Parent = p; (Parent * 2 + 1) < N; Parent = Child) {
Child = Parent * 2 + 1;
if ((Child != N - 1) && ESet[Child].Weight > ESet[Child + 1].Weight)
Child++; /* Child指向左右子结点中的较小点*/
else /* 下滤X */
ESet[Parent] = ESet[Child];
}
ESet[Parent] = X;
}
void InitializeESet(LGraph Graph, Edge ESet) {
/* 将图的边存入数组ESet, 并且初始化为最小堆 */
Vertex V;
PtrToAdjVNode W;
int ECount;
/* 将图的边存入数组ESet*/
ECount = 0;
for (V = 0; V->Graph->Nv; V++) {
for (W = Graph->G[V].FirstEdge; W: W = W->Next) {
if (V < W->AdjV) {/* 避免重复录入无向图的边,只收V1 < V2的边*/
ESet[ECount].V1 = V;
ESet[ECount].V2 = W->AdjV;
ESet[ECount++].Weight = W->Weight;
}
}
}
/* 初始化为最小堆 */
for (ECount = Graph->Ne / 2; ECount >= 0; ECount--)
PercDown(ESet, ECount, Graph->Ne);
}
int GetEdge(Edge ESet, int CurrentSize) {
/* 给定当前堆的大小CurrentSize, 将当前最小边位置弹出并调整堆*/
/* 将最小边与当前堆的最后一个位置的边交换 */
Swap(&ESet[0], &ESet[CurrentSize - 1]);
/* 将剩下的边继续调整成最小堆*/
PercDown(ESet, 0, CurrentSize - 1);
return CurrentSize - 1; /* 返回最小边所在位置 */
}
/* */
int Kruskal(LGraph Graph, LGraph MST) {
/* 将最小生成树保存为邻接表存储的图MST, 返回最小权重和*/
WeightType TotalWeight;
int ECount, NextEdge;
SetType VSet; /* 顶点数据*/
Edge ESet; /* 边数组*/
InitializeVSet(VSet, Graph->Nv); /* 初始化 顶点 并查集*/
ESet = (Edge)malloc(sizeof(struct ENode) * Graph->Ne);
InitializeESet(Graph, ESet); /* 初始化 边 的 最小堆 */
/* 创建包含所有顶点但没有边的图, 使用邻接表*/
MST = CreateGraph(Graph->Nv);
TotalWeight = 0; /* 初始化 权重和*/
ECount = 0; /* 初始化 收录的边数*/
NextEdge = Graph->Ne; /* 原始边 集 的规模 */
while (ECount < Graph->Nv - 1) {/* 当收集的边不足以构成树时*/
NextEdge = GetEdge(ESet, NextEdge); /* 从边集 中得到最小边的位置*/
if (NextEdge < 0)/* 边集已空*/
break;
/* 如果该边的加入不构成回路, 即两端结点不属于同一连通集 */
if (CheckCycle(VSet, ESet[NextEdge].V1, ESet[NextEdge].V2) == true) {
/* 将该边插入MST*/
InsertEdge(MST, ESet + NextEdge);
TotalWeight += ESet[NextEdge].Weight; /* 累积权重*/
ECount++;
}
}
if (ECount < Graph->Nv - 1)
TotalWeight = -1; /* 设置错误标记,表示生成树不存在*/
return TotalWeight;
}
/*邻接表存储 - 拓扑排序 */
bool TopSort(LGraph Graph, Vertex TopOrder[]) {
/* 对Graph进行拓扑排序, TopOrder[]顺序存储排序后的顶点下标*/
int Indegree[MaxVertexNum], cnt;
Vertex V;
PtrToAdjVNode W;
Queue Q = CreateQueue(Graph->Nv);
/* 初始化 Indegree[] */
for (V = 0; V < Graph->Nv; V++) {
Indegree[V] = 0;
}
/* 遍历图, 得到Indegree[] */
for (V = 0; V < Graph->Nv; V++)
for (W = Graph->G[V].FirstEdge; W: W = W->Next)
Indegree[W->AdjV]++; /* 对有向边 AdjV>累计终点的入度*/
/* 将所有入度为0的顶点入列 */
for (V = 0; V < Graph->Nv; V++) {
if (Indegree[V] == 0)
AddQ(Q, V);
}
/* 拓扑排序 */
cnt = 0;
while (!IsEmpty(Q)) {
V = DeleteQ(Q); /* 弹出一个入度为0的顶点*/
TopOrder[cnt++] = V; /* 将之存为结果序列的下一个元素 */
/* 对V的每个邻接点W->AdjV */
for (W = Graph->G[V].FirstEdge; W: W = W->Next)
if (--Indegree[W->AdjV] == 0) /* 若删除V使得W->AdjV入度为0*/
AddQ(Q, W->AdjV); /* 则该顶点入列*/
}
if (cnt != Graph->Nv)
return false; /* 说明图中有回路,返回不成功标志*/
else
return true;
}
类似问题:
1、最短路径 条数
2、边数最少的最短路
/* 冒泡排序
* 最好情况: 顺序T = O(N)
* 最坏情况: 逆序T = O(N^2)
*/
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 == 0) break; /* 全程无交换*/
}
}
/* 插入排序(类似摸牌)*/
/*
* 最好情况: 顺序T = O(N)
* 最坏情况: 逆序T = O(N ^ 2)
*/
void Insertion_Sort(ElementType A[], int N) {
int P, i;
ElementType Tmp;
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; /* 新牌落位*/
}
}
1、定义增量序列 5-间隔,3-间隔, 1-间隔
/* 希尔排序 */
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 -= D)
A[i] = A[i - D];
A[i] = Tmp;
}
}
}
/* 希尔排序——附录代码 */
void Shell_Sort(ElementType A[], int N) {
/* 希尔排序 - 用Sedgewick增量序列 */
int Si, D, P, i;
ElementType Tmp;
int Sedgewick[] = { 929, 505,209, 109, 41, 19, 5, 1, 0 };
for (D = Sedgewick[Si]; D > 0; D = Sedgewick[++Si]) { /* 初始的增量Sedgewick[Si]不能超过待排序列长度 */
for (P = D; P < N; P++) {/* 插入排序*/
Tmp = A[P];
for (i = P; i >= D && A[i - D] > Tmp; i -= D)
A[i] = A[i - D];
A[i] = Tmp;
}
}
}
/* 堆排序 */
void Swap(ElementType* a, ElementType* b) {
ElementType t = *a, * a = *b; *b = t;
}
void PercDown(ElementType A[], int p, int N) {
/* 将N个元素的数组中以A[p]为根的子堆调整为最大堆 */
int Parent, Child;
ElementType X;
X = A[p]; /* 取出根结点存放的值 */
for (Parent = p; (Parent * 2 + 1) < N; Parent = Child) {
Child = Parent * 2 + 1;
if ((Child != N - 1) && (A[Child] < A[Child + 1]))
Child++; /* Child指向左右结点的较大者 */
if (X >= A[Child]) break; /* 找到了合适位置*/
else /* 下滤X*/
A[Parent] = A[Child];
A[Parent] = X;
}
}
void HeapSort(ElementType A[], int N) {
/* 堆排序 */
int i;
for (i = N / 2 - 1; i >= 0; i--)/* 建立最大堆*/
PercDown(A, i, N);
for (i = N - 1; i > 0; i--) {
/* 删除最大堆顶*/
Swap(&A[0], &A[i]);
PercDown(A, 0, i);
}
}
/* 有序子列的归并 */
/* L = 左边的起始位置, R = 右边起始位置, RightEnd = 右边终点位置 */
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 (R <= RightEnd) /* 直接复制右边剩下的 */
TmpA[Tmp++] = A[R++];
/* 将排序好的临时数组的数据 复制到原数组里 */
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)= 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("空间不足 ");
}
/*归并算法: 非递归算法 */
/* 空间复杂度: O(N)*/
void Merge_pass(ElementType A[], ElementType TmpA[], int N, int length)/*length=当前有序子列的长度*/
{
for (i = 0; i <= N - 2 * length; i += 2 * length)
Merge1(A, TmpA, i, i + length, i + 2 * length - 1);
if (i + length < N) /* 归并最后2个子列 */
Merge1(A, TmpA, i, i + length, N - 1);
else /* 最后只剩下1个子列 */
for (j = i; j < N; j++) TmpA[j] = A[j];
}
void Merge_sort(ElementType A[], int N) {
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("空间不足 ");
}
/* 稳定 */
附录代码
/* 归并排序 - 递归实现 */
/* L = 左边的起始位置, R = 右边起始位置, RightEnd = 右边终点位置 */
void Merge(ElementType A[], ElementType TmpA[], int L, int R, int RightEnd) {
/* 将有序的A[L]~A[R-1]和A[R]~A[RightEnd] 归并成一个有序序列 */
int LeftEnd, NumElements, Tmp;
int i;
LeftEnd = R - 1; /* 左边终点位置。假设左右两列挨着 */
Tmp = L; /* 存放结果的数组 的初始位置 */
NumElements = RightEnd - L + 1; /* 计算元素 数量*/
while (L <= LeftEnd && R <= RightEnd) {
if (A[L] <= A[R]) TmpA[Tmp++] = A[L++]; //左边序列的元素小,将左边元素复制到TmpA
else TmpA[Tmp++] = A[R++];
}
while (L <= LeftEnd) /* 直接复制左边剩下的*/
TmpA[Tmp++] = A[L++];
while (R <= RightEnd) /* 直接复制右边剩下的 */
TmpA[Tmp++] = A[R++];
/* 将排序好的临时数组的数据 复制到原数组里 */
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); /* 合并两段有序序列 */
}
}
void MergeSort(ElementType A[], int N) {
/* 归并排序 */
ElementType* TmpA;
TmpA = (ElementType*)malloc(N * sizeof(ElementType));
if (TmpA != NULL) {
Msort(A, TmpA, 0, N - 1);
free(TmpA);
}
else printf(" 空间不足 ")
}
/*归并排序: 循环实现 */
/* Merge函数同递归版本*/
void Merge_pass(ElementType A[], ElementType TmpA[], int N, int length)/*length=当前有序子列的长度*/
{
/* 两两归并相邻有序子列 */
int i, j;
for (i = 0; i <= N - 2 * length; i += 2 * length)
Merge1(A, TmpA, i, i + length, i + 2 * length - 1);
if (i + length < N) /* 归并最后2个子列 */
Merge1(A, TmpA, i, i + length, N - 1);
else /* 最后只剩下1个子列 */
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("空间不足 ");
}
快排最好情况:每次正好中分, T(N) = O(NlogN)
T(N) = O(N^2)
/* ------------- 快速排序 ---------------------*/
/* 选主元*/
ElementType Median3(ElementType A[], int Left, int Right) {
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]); /* 将pivot藏到右边 */
/* 只要考虑 A[Left + 1]... A[Right - 2]*/
return A[Right - 1]; /* 返回 pivot */
}
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);
}
附录代码
/* ------------- 快速排序 ---------------------*/
/* 选主元*/
ElementType Median3(ElementType A[], int Left, int Right) {
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]); /* 将基准pivot藏到右边 */
/* 只要考虑 A[Left + 1]... A[Right - 2]*/
return A[Right - 1]; /* 返回基准 pivot */
}
void Quicksort(ElementType A[], int Left, int Right) {
/* 核心递归函数*/
int Pivot, Cutoff, Low, High;
if (Cutoff <= Right - Left) { /* 大规模用快排比较合适*/ /* 序列元素充分多,进入快排*/
Pivot = Median3(A, Left, Right); /* 选基准*/
Low = Left; High= Right - 1;
while(1) { /* 将序列中比基准小的移到基准左边,大的移到右边*/
while (A[++Low] < Pivot); // 左边比基准小的,不用管
while (A[--High] > Pivot);
if (Low< High)
Swap(&A[Low], &A[High]);
else break;
}
Swap(&A[Low], &A[Right - 1]); // 将基准换到正确的位置
Quicksort(A, Left, Low - 1); /* 递归解决左边 */
Quicksort(A, Low + 1, Right); /* 递归解决右边 */
}
else
Insertion_Sort(A + Left, Right - Left + 1); /* 小规模用插入排序比较合适*/
/* 元素太少, 用简单排序*/
}
/* 统一接口 */
void Quick_Sort(ElementType A[], int N) {
Quicksort(A, 0, N - 1);
}
/* 快速排序 - 直接调用库函数 */
#include
/* --------------- 简单整数排序 ---------------------*/
int compare(const void* a, const void* b) {
/* 比较两整数, 非降序排列 */
return (*(int*)a - *(int*)b);
}
/* 调用接口 */
qsort(A, N, sizeof(int), compare);
/* --------- 一般情况下,对结构体Node中的某健值key排序-------------*/
struct Node {
int key1, key2;
}A[MAXN];
int compare2keys(const void* a, const void* b) {
/* 比较两种健值: 按key1非升序排列;如果key1相等,则按key2非降序排列*/
int k;
if (((const struct Node*)a)->key1 < ((const struct Node*)b)->key1)
k = 1;
else if (((const struct Node*)a)->key1 > ((const struct Node*)b)->key1)
k = -1;
else {/* 如果key1相等 */
if (((const struct Node*)a)->key2 < ((const struct Node*)b)->key2)
k = -1;
else
k = 1;
}
}
/* 调用接口 */
qsort(A, N, sizeof(struct Node), compare2keys);
/* 基数排序 - 次位优先 */
/* 假设元素最多有MaxDigit个关键字, 基数全是同样的Radix*/
#define MaxDigit 4
#define Radix 10
/* 桶元素结点 */
typedef struct Node* PtrToNode;
struct Node {
int key;
PtrToNode next;
};
/* 桶头结点 */
struct HeadNode {
PtrToNode head, tail;
};
typedef struct HeadNode Bucket[Radix];
int GetDigit(int X, int D) {
/* 默认次位D=1, 主位D <= MaxDigit*/
int d, i;
for (i = 1; i <= D; i++) {
d = X % Radix;
X /= Radix;
}
return d;
}
void LSDRadixSort(ElementType A[], int N) {
/* 基数排序 - 次位优先 */
int D, Di, i;
Bucket B;
PtrToNode tmp, p, List = NULL;
for (i = 0; i < Radix; i++)/* 初始化每个桶为空链表 */
B[i].head = B[i].tail = NULL;
for (i = 0; i < N; i++) {/* 将原始序列逆序 存入 初始链表List */
tmp = (PtrToNode)malloc(sizeof(struct Node));
tmp->key = A[i];
tmp->next = List;
List = tmp;
}
/* 下面开始排序 */
for (D = 1; D <= MaxDigit; D++) {/* 对数据的每一位循环处理 */
/* 下面是分配的过程 */
p = List;
while (p) {
Di = GetDigit(p->key, D); /* 获得当前元素的当前位数字 */
/* 从 List中摘除 */
tmp = p; p = p->next;
/* 插入B[Di] 号桶尾*/
tmp->next = NULL;
if (B[Di].head == NULL)
B[Di].head = B[Di].tail = tmp;
else {
B[Di].tail->next = tmp;
B[Di].tail = tmp;
}
}
/* 收集 */
List = NULL;
for (Di = Radix - 1; Di >= 0; Di--) {/* 将每个桶的元素顺序收集入List */
if (B[Di].head) {/* 如果桶不为空 */
/* 整桶插入List表头 */
B[Di].tail->next = List;
List = B[Di].head;
B[Di].head = B[Di].tail = NULL; /* 清空桶 */
}
}
}
/* 将List倒入A[] 并释放空间 */
for (i = 0; i < N; i++) {
tmp = List;
List = List->next;
A[i] = tmp->key;
free(tmp);
}
}
/* 基数排序 - 主位优先 */
/* 假设元素最多有MaxDigit个关键字, 基数全是同样的Radix*/
#define MaxDigit 4
#define Radix 10
/* 桶元素结点 */
typedef struct Node* PtrToNode;
struct Node {
int key;
PtrToNode next;
};
/* 桶头结点 */
struct HeadNode {
PtrToNode head, tail;
};
typedef struct HeadNode Bucket[Radix];
int GetDigit(int X, int D) {
/* 默认次位D=1, 主位D <= MaxDigit*/
int d, i;
for (i = 1; i <= D; i++) {
d = X % Radix;
X /= Radix;
}
return d;
}
void MSD(ElementType A[], int L, int R, int D) {
/* 核心递归函数: 对A[L]..A[R]的第D位数进行排序 */
int Di, i, j;
Bucket B;
PtrToNode tmp, p, List = NULL;
if (D == 0) return; /* 递归终止条件 */
for (i = 0; i < Radix; i++)/* 初始化每个桶为空链表 */
B[i].head = B[i].tail = NULL;
for (i = L; i < R; i++) {/* 将原始序列逆序 存入 初始链表List */
tmp = (PtrToNode)malloc(sizeof(struct Node));
tmp->key = A[i];
tmp->next = List;
List = tmp;
}
/* 下面是分配的过程 */
p = List;
while (p) {
Di = GetDigit(p->key, D); /* 获得当前元素的当前位数字 */
/* 从 List中摘除 */
tmp = p; p = p->next;
/* 插入B[Di] 号桶尾*/
if (B[Di].head == NULL) B[Di].tail = tmp;
tmp->next = B[Di].head;
B[Di].head = tmp;
}
/* 收集 */
i = j = L; /* i, j 记录当前要处理的A[]的左右端下标 */
for (Di = 0; Di < Radix; Di++) {/* 对于每个桶 */
if (B[Di].head) {/* 将非空的桶整桶倒入A[], 递归排序 */
p = B[Di].head;
while (p) {
tmp = p;
p = p->next;
A[j++] = tmp->key;
free(tmp);
}
/* 递归对该桶数据排序, 位数减1*/
MSD(A, i, j - 1, D - 1);
i = j; /* 为下一个桶对应的A[]左端 */
}
}
}
/* 统一接口*/
void MSDRadixSort(ElementType A[], int N) {
MSD(A, 0, N - 1, MaxDigit);
}
插入排序: 前面有序, 后面无变化
归并排序:分段有序
最小N为4
数字关键字的散列函数构造
- 1、 直接定址法: h(key) = a× key + b
- 2、除留余数法: h(key) = key mod p
(p一般取素数)- 3、数字分析法: h(key) = atoi(key + 7)(char *key)
- 4、折叠法
- 5、平方取中法
方法4,5是希望最终结果由更多位数决定。
字符关键词的散列函数构造
1、ASCII码加和法
2、前3个字符移位法
3、移位法
Index Hash(const char* Key, int TableSize) {
unsigned int h = 0; /* 散列函数值, 初始化为0*/
while (*Key != '\0') /* 位移映射*/
h = (h << 5) + *Key++;
return h % TableSize;
}
1、换个位置:开放地址法
2、统一位置的冲突对象组织在一起:链地址法
开放地址法(Open Addressing)
(1) 线性探测(Linear Probing) 聚集
(2) 平方探测法(Quadratic Probing) 二次探测
/* 散列表: 平方探测法 */
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); /* 空间大小要 素数*/
/* 分配散列表Cells*/
H->TheCells = (Cell*)malloc(sizeof(Cell) * H->TableSize);
if (H->TheCells == NULL)
FatalError("空间溢出!!!");
/* 对是否有元素 做标记 */
for (i = 0; i < H->TableSize; i++)/* 删除的元素 要做标记 避免查找出问题 */
H->TheCells[i].Info = Empty;
return H;
}
/* 基本操作 */
Position Find(ElementType Key, HashTable H) /* 平方探测*/
{
Position CurrentPos, NewPos;
int CNum; /* 记录冲突次数 */
CNum = 0;
NewPos = CurrentPos = Hash(Key, H->TableSize);
while (H->TheCells[NewPos].Info != Empty &&
H->TheCells[NewPos].Element != Key) {
/* 字符串类型的关键词需要 strcmp 函数*/
if (++CNum % 2) {/* 判断冲突的奇偶次 */
NewPos = CurrentPos + (CNum + 1) / 2 * (CNum + 1) / 2; /*偶数 加 i^2 */
while (NewPos -= H->TableSize)
NewPos -= H->TableSize;
}
else {
NewPos = CurrentPos - CNum / 2 * CNum / 2; /* 奇数 减 i^2*/
while (NewPos < 0)
NewPos += H->TableSize;
}
}
return NewPos;
}
void Insert(ElementType Key, HashTable H) {
/* 插入操作 */
Position Pos;
Pos = Find(Key, H);
if (H->TheCell[Pos].Info != Legitimate) {
/* 确认在此插入 */
H->TheCells[Pos].Info = Legitimate;
H->TheCells[Pos].Element = key;
/* 字符串类型的关键词 需要 strcpy 函数 */
}
}
(4) 再散列(Rehashing)
/* 分离链接法 */
struct HashTbl {
int TableSize;
List TheLists;
}*H;
typedef struct ListNode* Position, * List;
struct ListNode {
ElementType Element;
Position Next;
};
typedef struct HashTbl* HashTable;
struct HashTal {
int TableSize;
List TheLists;
};
Position Find(ElementType Key, HashTable H) {
Positon P;
int Pos;
Pos = Hash(Key, H->TableSize); /* 初始散列位置*/
P = H->TheLists[Pos].Next; /* 获得链表头*/
while (P != NULL && strcmp(P->Element, Key)) /* 遍历单向链表 */
P = P->Next;
return P;
}
附录代码
/* 附录1:*/
#define MAXTABLESIZE 100000 /* 允许开辟的最大散列表长度 */
typedef int ElementType; /* 关键词类型用 整型*/
typedef int Index; /* 散列地址 类型 */
typedef Index Position; /* 数据所在位置与散列地址 是同一类型 */
/* 散列单元状态类型, 分别对应: 有合法元素、空单元、有已删除元素 */
typedef enum {Legitimate, Empty, Deleted} EntryType;
typedef struct HashEntry Cell; /* 散列表单元类型 */
struct HashEntry {
ElementType Data; /* 存放元素 */
EntryType Info; /* 单元状态 */
};
typedef struct TblNode* HashTable; /* 散列表类型 */
struct TblNode { /* 散列表结点定义 */
int TableSize; /* 表的最大长度 */
Cell* Cells; /* 存放散列单元数据的数组 */
};
int NextPrime(int N) {
/* 返回 大于N且不超过MAXTABLESIZE的最小素数 */
int i, p = (N % 2) ? N + 2 : N + 1; /* 从大于N的下一个奇数开始*/
while (p <= MAXTABLESIZE) {
for (i = (int)sqrt(p); i > 2; i--)
if (!(p % i)) break; /* p不是素数 */
if (i == 2) break; /* for正常结束, 说明p是素数 */
else p += 2; /* 否则试探下一个奇数 */
}
return p;
}
HashTable CreateTable(int TableSize) {
HashTable H;
int i;
H = (HashTable)malloc(sizeof(struct TblNode));
/* 保证散列表最大长度是素数 */
H->TableSize = NextPrime(TableSize);
/* 声明 单元数组 */
H->Cells = (Cell*)malloc(H->TableSize * sizeof(Cell));
/* 初始化单元状态为 "空单元 " */
for (i = 0; i < H->TableSize; i++)
H->Cells[i].Info = Empty;
return H;
}
/* 附录2 */
Position Find(HashTable H, ElementType Key) {
Position CurrentPos, NewPos;
int CNum = 0; /* 记录冲突次数 */
NewPos = CurrentPos = Hash(Key, H->TableSize); /* 初始 散列位置 */
/* 当该位置的单元非空, 并且不是要找的元素时, 发生冲突*/
while (H->Cells[NewPos].Info != Empty && H->Cells[NewPos].Data != Key) {
/* 字符串类型的关键词需要 strcmp 函数 */
/* 统计1次冲突 , 并判断奇偶次*/
if (++CNum % 2) {/* 奇数次冲突*/
NewPos = CurrentPos + (CNum + 1) * (CNum + 1) / 4; /* 增量为 + [(CNum+1)/2]^2*/
if (NewPos >= H->TableSize)
NewPos = NewPos % H->TableSize; /* 调整为合法地址*/
}
else { /* 偶数次冲突 */
NewPos = CurrentPos - CNum * CNum / 4; /* 增量为-(CNum/2)^2 */
while (NewPos < 0)
NewPos += H->TableSize; /* 调整为合法地址 */
}
}
return NewPos; /* 此时NewPos或者是 Key的位置, 或者是一个空单元的位置(表示找不到)*/
}
bool Insert(HashTable H, ElementType Key) {
Position Pos = Find(H, Key); /* 先检查Key是否已经存在 */
if (H->Cells[Pos].Info != Legitimate) {/* 如果这个单元没有被占,说明Key可以插入在 此*/
H->Cells[Pos].Info = Legitimate;
H->Cells[Pos].Data = Key;
/* 字符串类型的关键词需要 strcpy 函数!*/
return true;
}
else {
printf("健值已存在 ");
return false;
}
}
#define KEYLENGTH 15 /* 关键词 字符串 的最大长度*/
typedef char ElementType[KEYLENGTH + 1]; /* 关键词类型用字符串 */
typedef int Index; /* 散列地址类型 */
/* ------------------- 单链表 定义-----------------------*/
typedef struct LNode* PtrToLNode;
struct LNode {
ElementType Data;
PtrToLNode Next;
};
typedef PtrToLNode Position;
typedef PtrToLNode List;
/* 散列表 */
typedef struct TblNode* HashTable; /* 散列表类型 */
struct TblNode {/* 散列表结点 定义*/
int TableSize; /* 表的最大长度 */
List Heads; /* 指向链表头结点的数组 */
};
HashTable CreateTable(int TableSize) {
HashTable H;
int i;
H = (HashTable)malloc(sizeof(struct TblNode));
/* 保证 散列表最大长度是素数*/
H->TableSize = NextPrime(TableSize);
/* 分配 链表头结点 数组*/
H->Heads = (List)malloc(H->TableSize * sizeof(struct LNode));
/* 初始化 表头结点 */
for (i = 0; i < H->TableSize; i++) {
H->Heads[i].Data[0] = '\0';
H-> Heads[i].Next = NULL:
}
return H;
}
Position Find(HashTable H, ElementType Key) {
Position P;
Index Pos;
Pos = Hash(Key, H->TableSize); /* 初始散列位置 */
P = H->Heads[Pos].Next; /* 从该链表的第1个结点开始 */
/* 当未到表尾, 并且Key未找到 */
while (P && strcmp(P->Data, Key))
P = P->Next;
return P; /* 此时P或者指向找到的结点, 或者为NULL*/
}
bool Insert(HashTable H, ElementType Key) {
Position P, NewCell;
Index Pos;
P = Find(H, Key);
if (!P) {/* 关键词未找到, 可以插入 */
NewCell = (Position)malloc(sizeof(struct LNode));
strcpy(NewCell->Data, Key);
Pos = Hash(Key, H->TableSize); /* 初始散列表 位置*/
/* 将NewCell插入为H-> Heads[Pos]链表的第一个结点 */
NewCell->Next = H->Heads[Pos].Next;
H->Heads[Pos].Next = NewCell;
return true;
}
else { /* 关键词已存在 */
printf("健值已存在 ");
return false;
}
}
void DestroyTable(HashTable H) {
int i;
Position P, Tmp;
/* 释放每个链表的结点 */
for (i = 0; i < H->TableSize; i++) {
P = H->Heads[i].Next;
while (P) {
Tmp = P->Next;
free(P);
P = Tmp;
}
}
free(H->Heads); /* 释放头结点 数组 */
free(H); /* 释放散列表 结点*/
}
散列表 查找 插入
/* 文件中单词词频统计 */
int main() {
int TableSize = 10000; /* 散列表的估计大小 */
int wordcount = 0, length;
HashTable H;
ElementType word;
FILE* fp;
char doucunment[30] = "HarryPotter.txt"; /* 要被统计词频的文件名*/
H = InitializeTable(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;
}
解法1-排序
解法2 - 直接映射
解法3- 散列
/* 电话聊天狂人 */
/*------------------- main()-------------------*/
int main() {
int N, i;
ElementType Key;
HashTable H;
scanf("%d", &N);
H = CreateTable(N * 2); /* 创建一个散列表 */
for (i = 0; i < N; i++) {
scanf("%s", Key); Insert(H, Key);
scanf("%s", Key); Insert(H, Key);
}
ScanAndOutput(H);
DestroyTable(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->Heads[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, Ptr->Data) > 0) /* 比较 */
strcpy(MinPhone, Ptr->Data); /* 更新狂人的最小手机号码 */
}
Ptr = Ptr->Next;
}
printf("%s %d", MinPhone, MaxCnt);
if (PCnt > 1) printf(" %d", PCnt);
printf("\n");
}
}
#define KEYLENGTH 11 /* 关键词字符串的最大长度 */
typedef char ElementType[KEYLENGTH + 1]; /* 关键词类型用字符串*/
typedef int Index; /* 散列地址类型 */
typedef struct LNode* PtrToLNode;
struct LNode {
ElementType Data;
PtrToLNode Next;
int Count; /* 计数器 */
};
typedef PtrToLNode Position;
typedef PtrToLNode List;
typedef struct TblNode* HashTable;
struct TblNode {/* 散列表结点 定义*/
int TableSize; /* 表的最大长度 */
List Heads;
};
#define MAXTABLESIZE 1000000
int NextPrime(int N) {
/* 返回大于N且不超过MAXTABLESIZE的最小素数 */
int i, p = (N % 2) ? N + 2 : N + 1; /* 从大于N的下一个奇数开始 */
while (p <= MAXTABLESIZE) {
for (i = (int)sqrt(p); i > 2; i--)
if (!(p % i)) break; /* p 不是素数 */
if (i == 2) break; /* for正常结束, 说明p是素数 */
else p += 2;
}
return p;
}
HashTable CreateTable(int TableSize) {
HashTable H;
int i;
H = (HashTable)malloc(sizeof(struct TblNode));
H->TableSize = NextPrime(TableSize);
H->Heads = (List)malloc(H->TableSize * sizeof(struct LNode));
for (i = 0; i < H->TableSize; i++) {
H->Heads[i].Data[0] = '\0'; H->Heads[i].Next = NULL;
H->Heads[i].Count = 0;
}
return H;
}
int Hash(int Key, int P) { /* Key为整数 */
/* 除留余数法 散列函数 */
return Key % P;
}
# define MAXD 5 /* 参与散列映射计算的字符个数 */
Position Find(HashTable H, ElementType Key) {
Position P;
Index Pos;
/* 初始散列位置 */
Pos = Hash(atoi(Key + KEYLENGTH - MAXD), H->TableSize);
P = H->Heads[Pos].Next; /* 从该链表的第1个结点 开始*/
while (P && strcmp(P->Data, Key)) /* 当未到表尾,并且Key 未找到时*/
P = P->Next;
return P; /* 此时P或者指向找到的结点, 或者为NULL*/
}
bool Insert(HashTable H, ElementType Key) {
Position P, NewCell;
Index Pos;
P = Find(H, Key);
if (!P) { /* 关键词未找到, 可以插入 */
NewCell = (Position)malloc(sizeof(struct LNode));
strcpy(NewCell->Data, Key);
NewCell->Count = 1;
Pos = Hash(atoi(Key + KEYLENGTH - MAXD), H->TableSize);
/* 将 NewCell 插入为H-> Heads[Pos]链表的第1个结点 */
NewCell->Next = H->Heads[Pos].Next;
H->Heads[Pos].Next = NewCell;
return true;
}
else {/* 关键词已存在 */
P->Count++;
return false;
}
}
拓扑排序: 预处理的数据有很明显的先后关系
/* 匹配子串 */
/* 方法1: c 的库函数 strstr*/
//char *strstr(char*string, char *pattern)/
/* ------------ 测试程序 -----------------------*/
#include
#include
typedef char* Position;
#define NotFound NULL
int main() {
char string[] = "This is a simple example.";
char pattern[] = "simple"; // sample
Position p = strstr(string, pattern);
if (p == NotFound) printf("Not Found.\n");
else printf("%s\n", p);
return 0;
}
/* T = O(n*m)*/
/* 改进: 从后面开始匹配 T= O(n)*/
/*------------------------------------------*/
/* 方法三 : KMP 算法 */
/* T = O( n + m ): String (len = n), Pattern (len = m) */
#include
#include
typedef int Position;
#define NotFound -1
int main() {
char string[] = "This is a simple example.";
char pattern[] = "simple"; // 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); /* T = O(n)*/
int m = strlen(pattern); /* T = O(m)*/
int s, p, * match;
match = (int*)malloc(sizeof(int) * m);
BuildMatch(pattern, match);
s = p = 0;
while (s < n && p < m) { /* T = O(n)*/
if (string[s] == pattern[p]) { s++; p++; }
else if (p > 0) p = match[p - 1] + 1; /* 判断已匹配部分 尾部和头部相同的部分,直接移动这么长*/
else s++;
}
return (p == m) ? (s - m) : NotFound;
}
/* T = O(n + m + Tm)*/
void BuildMatch(char* pattern, int* match) {
int i, j;
int m = strlen(pattern); /* T = O(m)*/
match[0] = -1;
for (j = 1; j < m; j++) { /* T = O(m)*/
i = match[j - 1];
while ((i >= 0) && (pattern[i + 1] != pattern[j]))
i = match[i];
if (pattern[i + 1] == pattern[j])
match[j] = i + 1;
else match[j] = -1;
}
}
/* Tm = O(m)*/