C 数据结构3 —— 排序\哈希查找\KMP

1. 插入排序

1.1 直接插入排序

// 插入排序,从小到大排序,升序
void InsertSort(ElemType A[], int n)
{
	int i, j;
	// 24 66 94 2 15 74 28 51 22 18 2
	for (i = 2; i <= n; i++) // 第0个元素放置哨兵,i = 1是自然有序,拿第2个元素往前面插
	{
		if (A[i] < A[i - 1])
		{
			A[0] = A[i];// 放到暂存位置,A[0]是既是暂存也是哨兵位置
			for (j = i - 1; A[0] < A[j]; --j) // 如果A[0]<A[j],移动元素,内层循环控制有序序列中的每一个元素和要插入的元素比较
				A[j + 1] = A[j];// 不断地往后面赋值
			A[j + 1] = A[0];// 因为结束前做1次--j,所以要j+1,把暂存元素插入到对应位置
		}
	}
}

在这里插入图片描述

1.2 折半插入排序

// 折半插入排序 考的少
void MidInsertSort(ElemType A[], int n)
{
	int i, j, low, high, mid;
	for (i = 2; i <= n; i++)
	{
		A[0] = A[i];
		low = 1; high = i - 1;// low有序序列的开始,high有序序列的最后
		while (low <= high)// 先通过二分查找找到待插入的位置
		{
			mid = (low + high) / 2;
			if (A[mid] > A[0])
				high = mid - 1;
			else
				low = mid + 1;
		}
		for (j = i - 1; j >= high + 1; --j)
			A[j + 1] = A[j];
		A[high + 1] = A[0];
	}
}

1.3 希尔排序

// 希尔排序
// 多轮插入排序,考大题的概率小,效率不如快排和堆排
// 小题考步长如何变化
void ShellSort(ElemType A[], int n)
{
	int dk, i, j;
	for (dk = n / 2; dk >= 1; dk = dk / 2)// 步长变化
	{
		for (i = dk + 1; i <= n; ++i)// 以dk为步长进行插入排序
		{
			if (A[i] < A[i - dk])
			{
				A[0] = A[i];
				for (j = i - dk; j > 0 && A[0] < A[j]; j = j - dk)
					A[j + dk] = A[j];
				A[j + dk] = A[0];// 类比插入排序,将1用dk替换
			}
		}
	}
}

在这里插入图片描述

1.4 总程序

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

typedef int ElemType;
typedef struct {
	ElemType* elem;// 整型指针
	int TableLen;// 元素空间
}SSTable;// 结构体别名

void ST_Init(SSTable& ST, int len)// 初始化
{
	ST.TableLen = len + 1;// 实际申请11个元素的空间
	ST.elem = (ElemType*)malloc(sizeof(ElemType) * ST.TableLen);// 数组长度
	int i;
	srand(time(NULL));
	for (i = 0; i < ST.TableLen; i++)
	{
		ST.elem[i] = rand() % 100;// 随机11个数,但第1个元素时没有用到的
	}
}

void ST_print(SSTable ST)
{
	for (int i = 0; i < ST.TableLen; i++)
	{
		printf("%3d", ST.elem[i]);
	}
	printf("\n");
}

// 插入排序,从小到大排序,升序
void InsertSort(ElemType A[], int n)
{
	int i, j;
	// 24 66 94 2 15 74 28 51 22 18 2
	for (i = 2; i <= n; i++) // 第0个元素放置哨兵,i = 1是自然有序,拿第2个元素往前面插
	{
		if (A[i] < A[i - 1])
		{
			A[0] = A[i];// 放到暂存位置,A[0]是既是暂存也是哨兵位置
			for (j = i - 1; A[0] < A[j]; --j) // 如果A[0]<A[j],移动元素,内层循环控制有序序列中的每一个元素和要插入的元素比较
				A[j + 1] = A[j];// 不断地往后面赋值
			A[j + 1] = A[0];// 因为结束前做1次--j,所以要j+1,把暂存元素插入到对应位置
		}
	}
}

// 折半查找 插入排序 考的少
void MidInsertSort(ElemType A[], int n)
{
	int i, j, low, high, mid;
	for (i = 2; i <= n; i++)
	{
		A[0] = A[i];
		low = 1; high = i - 1;// low有序序列的开始,high有序序列的最后
		while (low <= high)// 先通过二分查找找到待插入的位置
		{
			mid = (low + high) / 2;
			if (A[mid] > A[0])
				high = mid - 1;
			else
				low = mid + 1;
		}
		for (j = i - 1; j >= high + 1; --j)
			A[j + 1] = A[j];
		A[high + 1] = A[0];
	}
}

// 希尔排序
// 多轮插入排序,考大题的概率小,效率不如快排和堆排
// 小题考步长如何变化
void ShellSort(ElemType A[], int n)
{
	int dk, i, j;
	for (dk = n / 2; dk >= 1; dk = dk / 2)// 步长变化
	{
		for (i = dk + 1; i <= n; ++i)// 以dk为步长进行插入排序
		{
			if (A[i] < A[i - dk])
			{
				A[0] = A[i];
				for (j = i - dk; j > 0 && A[0] < A[j]; j = j - dk)
					A[j + dk] = A[j];
				A[j + dk] = A[0];// 类比插入排序,将1用dk替换
			}
		}
	}
}

int main()
{
	SSTable ST;
	ST_Init(ST, 10);// 实际申请11个空间
	ST_print(ST);
	InsertSort(ST.elem, 10);
	//MidInsertSort(ST.elem,10);
	//ShellSort(ST.elem,10);
	ST_print(ST);
	system("pause");
}

1.5 冒泡排序&快速排序

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<string.h>

// 包括冒泡排序和快速排序

typedef int ElemType;
typedef struct
{
	ElemType* elem;// 存储元素的起始地址
	int TableLen;// 元素个数
}SSTable;

// 初始化,生成随机数
void ST_Init(SSTable& ST, int len)// 初始化,申请了空间
{
	ST.TableLen = len;
	ST.elem = (ElemType*)malloc(sizeof(ElemType) * ST.TableLen);// int是4个字节,Len是10个长度
	int i;
	srand(time(NULL));// 随机数生成,每一次执行代码就会得到随机的10个元素
	for (i = 0; i < ST.TableLen; i++)
	{
		ST.elem[i] = rand() % 100;// 生成的数都是0~99之间
	}
}

void ST_print(SSTable ST)
{
	for (int i = 0; i < ST.TableLen; i++)
	{
		printf("%d ", ST.elem[i]);
	}
}

void swap(ElemType& a, ElemType& b)
{
	ElemType tmp;
	tmp = a;
	a = b;
	b = tmp;
}

// 冒泡算法
void BubbleSort(ElemType A[], int n)
{
	int i, j;
	bool flag;
	for (i = 0; i < n-1; i++ )// i是控制有多少个有序了, i最多访问到8
	{
		flag = false;
		for (j = n - 1; j > i ; j--)// 内存控制比较,交换;把最小值放到最前面
		{
			if (A[j - 1] > A[j])
			{
				swap(A[j - 1], A[j]);
				flag = true;
			}
		}
		if (false == flag)
		{
			return;// 如果外层排序为有序,则退出
		}
	}
}

// 快排算法(交换法)
// i 用来遍历数组的下标,k 表示比 40 小的元素将要存的下标
int Partition1(int* arr, int left, int right)
{
	int k, i;
	k = i = left;
	for (i < right; i++;)
	{
		if (arr[i] < arr[right])
		{
			swap(arr[i], arr[k]);
			k++;
		}
	}
	swap(arr[k], arr[right]);
	return k;
}

// 64, 94, 95, 79, 69, 84, 18, 22, 12, 78
// 比64小的放在左边,比64大的放在右边
// 快排算法 挖坑法,王道书上使用的方法,最坐标作为分割值
int Partition2(ElemType A[], int low, int high)
{
	ElemType pivot = A[low];// 把最左边的值暂存起来
	while (low < high)
	{
		while (low < high && A[high] >= pivot)// 让high从最右边开始往左找,找到比分割值pivot小,分割结束
			--high;// --high表示先做减减,再做赋值操作
		A[low] = A[high];// A[low]的值已经赋给变量pivot,此时是空的
		while (low < high && A[low] <= pivot)// 让low从最左边开始找,找到比分割值大,结束
			++low;// 如果没找到比pivot大的值,low会一直加加,直到找到比分割值大的,然后赋值
		A[high] = A[low];
	}
	A[low] = pivot;
	return low;
}

// 快排递归实现
void QuickSort(ElemType A[], int low, int high)
{
	if (low < high)
	{
		int pivotpos = Partition1(A, low, high);// 分割点左边的元素要比分割点小,右边的比分割点大
		QuickSort(A, low, pivotpos - 1);
		QuickSort(A, pivotpos + 1, high);
	}
}

int main()
{
	SSTable ST;
	ElemType A[10] = { 64, 94, 95, 79, 69, 84, 18, 22, 12, 78 };
	ST_Init(ST, 10);// 初始化
	memcpy(ST.elem, A, sizeof(A));// 内存拷贝copy接口,当copy整型数组或者浮点型时,要用memcpy
	// memcpy是一个字节一个字节拷贝,A有多大就拷贝多大
	// 使用memecpy可以降低写代码的难度,让数据固定下来,排序算法写好后,注释memcpy注释代码即可
	ST_print(ST);
	BubbleSort(ST.elem,10);// 冒泡排序
	QuickSort(ST.elem, 0, 9);// 快速排序
	ST_print(ST);
	system("pause");
}

2. 哈希查找

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>

#define MaxKey 1000 // 定义哈希表的大小
int hash(const char* key)// 哈希函数
{
	int h = 0, g;
	while (*key)
	{
		h = (h << 4) + *key++;
		g = h & 0xf0000000;
		if (g)
		{
			h ^= g >> 24;
		}
		h &= ~g;
	}
	return h % MaxKey;
}

int main()
{
	// 这里是常量,字符指针数组,存了5个字符指针,每个指针指向字符串
	const char* pStr[5] = { "xiongda","lele","hanmeimei","wangdao","fenghua"};
	int i;
	const char* pHash_table[MaxKey] = { NULL };// 哈希表一定是数组,只有数组支持随机访问,哈希表
	for (i = 0; i < 5; i++)
	{
		printf("%s is key = %d\n", pStr[i], hash(pStr[i]));// 算出哈希值并且打印
		pHash_table[hash(pStr[i])] = pStr[i];// 存入哈希表
	}
	return 0;
}

3. KMP-字符串匹配模式

在这里插入图片描述

  • next数组
    在这里插入图片描述
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef char* SString;
// S abcabaaabaabcac
// T abaabcac
// 为什么从1的位置开始比较,因为0号位置存储了字符串的长度
int Index(SString S, SString T)
{
	int i = 1, j = 1;
	while (i <= S[0] && j <= T[0])
	{
		if (S[i] == T[i])
		{
			++i, ++j;// 继续比较后续字符
		}
		else
		{
			i = i - j + 2;
			j = 1;// 指针后退重新开始匹配
		}
	}
	if (j > T[0]) return i - T[0];// 匹配成功
}

// i游标,遍历T
void get_next(char T[], int next[])
{
	int i = 1;
	next[1] = 0;// 恒为零
	int j = 0;
	// abaabcac
	while (i < T[0])// T[0]记录字符串的长度
	{
		if (j == 0 || T[i] == T[j])// j = 0,表示再次回到了开头
		{
			++i; 
			++j;
			next[i] = j;// 记录出现重复的位置
		}
		else
		{
			j = next[j];// 不相同,找个位置重新比较
		}
	}
}

int KMP(char S[], char T[], int next[], int pos)
{
	int i = pos;// 开始查找的起始位置
	int j = 1;
	while (i <= S[0] && j <= T[0])
	{
		if (j == 0 || S[i] == T[j])// 相等各自加加,往后走
		{
			++i;
			++j;
		}
		else // 不等,就回退 next[j] 的位置
		{
			j = next[j];
		}
	}
	if (j > T[0])// 说明对比成功
	{
		return i - T[0];
	}
	else
		return 0;
}

int main()
{
	// 字符串的初始化
	char S[256];
	char T[10];
	int next[10];
	int pos;
	S[0] = strlen("abcabaaabaabcac");// strlen里有多少个字符
	strcpy(S + 1, "abcabaaabaabcac");
	T[0] = strlen("abaabcac");
	strcpy(T + 1, "abaabcac");
	pos = Index(S, T);// 暴力匹配
	if (pos)
	{
		printf("匹配成功,位置为%d\n", pos);
	}
	else
	{
		printf("未匹配\n");
	}
	get_next(T, next);// 算出next数组
	pos = KMP(S, T, next, 1);
	if (pos)
	{
		printf("匹配成功,位置为%d\n", pos);
	}
	else
	{
		printf("未匹配\n");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2021 Nqq

你的鼓励是我学习的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值