#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define swap(a,b) (c = a; a = b; b = c)
#define swap1(a,b) ( (a) = (a) + (b); (b) = (a) - (b); (a) = (a) - (b) ) //可能a+b 会溢出
#define swap2(a,b) ( (a) = (a) ^ (b); (b) = (a) ^ (b); (a) = (a) ^ (b) ) //不会溢出
#define get_ave(a,b) (a + (b-a) / 2) // ( (a+b) / 2 ) 第二种 溢出概率会小点
//选择排序:每次循环获取的是 当前循环内的最小值的位置下标
// 每次得到下标以后 就和当前循环基准 比较 是否进行交换
// 得到的最小值 存于放于基准位置
// 之后将基准的下一位置 作为基准继续重复以上
int select_sort(int a[],int len)
{
int i,k;
int j;
int temp;
for(i = 0;i < len -1;i++)//共需要 len-1 个返回
{
k = i;//记录每次 循环下 存放最小值的下标
for(j = i+1;j < len ;j++)//获取每个循环内最小数 的下标
{
if(a[k] > a[j])//从0 ~ len-1 开始比 获得更小值的下标存于k中
{
k = j;
}
}
if(k != i)//当最小数 不是 循环的基准位置 交换两个位置 接着寻找第二个更小的数
{
temp = a[i];
a[i] = a[k];
a[k] = temp;
}
}
for(i = 0; i < len;i++)
{
printf("%2d ",a[i]);
}
putchar(10);
return 0;
}
//选择原理: 每次从头开始 依次比较 得到最大的数 存于末尾
//
int maopao(int a[],int len)
{
int i;int j;int temp;
for(i = 0;i < len-1;i++)//一共需要比较 len -1个来回
{
for(j = 0;j < len-1-i; j++)//每一个循环中第一个数都得一次跟后面的数进行比较
//随着循环的增加 要比较的数开始减少
{
if(a[j] > a[j+1])//前大于后
{
temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
return 0;
}
//快速排序:① 快排:设置准点 将大于基准点的数放于基准点左边 小于基准点的数,放于基准点右边
// ② 对基准点左边的数进行递归快排 当只剩一个数的时候 此时left > right 递归结束
// ③ 对基准点右边的数进行递归快排 当只剩一个数的时候 此时left > right 递归结束
//一次快排
int get_mid(int a[],int left,int right)//快排 填坑法
{
int base = a[left];//设置基准点
while(left < right)
{
//这里等于 一定要加上 当你没加这个等于的时候 当这一轮结束时
//下一轮开始时如果 出现的基准点作为全场最大的数 此时while 的两个条件都不满足 left和right 都不会变化
// 使得最外层while陷入一个死循环的状态
while( (a[right] >= base) && (left < right) ) right--; //出现大于等于基准点的数时 右边 开始左移
a[left] = a[right];//当出现比基准点小的数时,放入左边基准点空位
//此时 右边出现空位
while( (a[left] <= base) && (left < right) ) left++;//出现小于等于 基准点的数 时 左边下标开始右移
a[right] = a[left]; //当出现 比基准点大的数的时候 放入右边空位
//此时左边出现空位
}//不断循环 直至所有的数 被基准点 左右完全分割完毕后 跳出循坏
a[left] = base;//归位
return left;
}
// 快排
void quick_sort(int a[],int left,int right)
{
if(left < right)//或 if(left > right ) return ;
{
int mid =get_mid(a,left,right);//快排的基准点分割
quick_sort(a,left,mid-1);//当快排只剩一个数时 返回的要么是mid= left 或mid=right 此时快排条件left>right的左边快排不满足 就会结束 进入右边快排
quick_sort(a,mid+1,right);//每进行一次递归 右边快排 都会进入左边快排 进行mid判断 当右边快排只剩一个数时,mid= left 或mid=right 此时快排条件left>right的右边快排不满足 就会结束 再次进入左边快排
//知道左右两边快排 都结束以后那么排序完成
}
}
//插入排序
//把数据分成两部分 一边是有序 一边是无序 (正常把第一个数作为有序的开始)
//从第二个数开始 也就是无序的第一个数 和 它前一个数比较(有序的第一个数) 比它小的话 就有序部分后移一个数 将比较的数 插入空出的有序部分的位置 此时有序部分变为两个数
// 当比它大的话 继续比较下一个无序部分的数
//这样通过一次循环 多次比较 解决排序问题 常用于数据量较少的排序 和 冒泡差不多
void insert_sort1(int a[],int len)
{
for(int i = 1;i<len;i++)//默认第一个数有序 从第二个开始排
{
int value = a[i];//value 表示 要排序的数
int end = i-1;//被比较的 有序部分 数的下标 当i = 1;跟 a[0]比较 i=2,要跟a[1] a[0]比较
//用于记录 要插入的位置
while(end >= 0 && a[end] > value) //end用于记录 每个数的 比较次数 同时用于比较 要插入的数的值
//当end = -1表示 比较结束
//当有序部分末尾的数 比 要插入的数大时 往前 继续比较
{
a[end+1] = a[end]; //后面的数 往后挪一个位置
end--;
}
a[end + 1] = value;
}
}
//插入排序2 优化
//
//
void insert_sort2(int a[],int len)
{
for(int i = 1;i<len;i++)// a[1] ~ a[len-1]是需要排序的 无序部分的数
//a[0] 初始 作为 有序部分
{
int temp = a[i];//记录 要排序的值
//要排序的数值 的前一个 就是有序部分的最大值
int j = i;
for(; j>0 && temp < a [j-1] ;j--) // 排序的值 与有序部分(从后往前)依次比较 当前一个值小(也就是比有序部分最大值小)
//就进入循环 将前一个值 后移一位 不要怕覆盖 因为它覆盖的是要排序的值
// 而这个值已经在前面保存了
{
a[j] = a[j-1];//后移 留出空位
}
a[j] = temp;
}
}
///希尔排序: 插入排序的改进 数据量越大 差距越明显
// 一:它通过增量d 来对 原序列进行分组设定 得到 它有几组 也就是几个部分 增量初值为元素总数的一半 每次循环减少自身一半 直到0
// 二:每次分组完 都对每个组内元素 进行直接插入排序
//重复一二 继续减小增量 直到增量为d = d/2 =0 排序结束
//
void shell_sort(int a[],int len)//
{
for(int d = len/2; d>0; d/=2) //增量控制
{
for(int i = d;i < len;i++)//组内元素 的插入排序 这里获取的是 后半部分下标遍历
{
int temp = a[i];
int j = i;
for(;j-d >= 0 && temp <a[j-d];j-=d)//后比前大 前往后移
{
a[j] = a[j-d];
}
a[j] = temp;
}
}
}
//折半查找法 又叫二分查找
//1. 指针有序序列进行的查找 通过定值 k 确认查找的范围 每次查找 将搜索空间减半
int binarySearch1(int a[],int len,int data)
{
int low = 0,high = len-1; //查找区间 每次查找 区间减半 当找到 时 就返回 或直到区间为0 查找结束
while(low <= high)
{
//int k = (low + high) >>1; //获取k值 可能会溢出
int k = low + (high -low) /2;
if(data < a[k])
{
high = k-1;//设定范围
}
else if(data > a[k])
{
low = k + 1;
}
else //相等 时 说明找到了 直接返回位置
{
return k;
}
}
return -1;//跳出循环 说明没炸到
}
// 折半查找 利用递归
int binarySearch(int a[],int len ,int low,int high,int data)
{
if( low <= high)
{
int k = (low + high) / 2;
if(data < a[k])
{
binarySearch(a,len,low,k-1,data);
}
else if(data > a[k])
{
binarySearch(a,len,k+1,high,data);
}
else //相等 时 说明找到了 直接返回位置
{
return k;
}
}
else
return -1;
}
// 哈希查找 通过建立 记录的关键字 与 存放地址 间的联系
// 1.哈希函数选择: 尽可能将记录点均匀化 减少冲突
// 主要有 直接地址法 数字分析法 除留取余法 平方取中法 叠加法 随机函数法
// 2.冲突的处理方法: 开放地址法: 增量的设定:线性探查法 二次探查法
// 链地址法 :将冲突通过链表 挂载到 冲突位置 中
//这里 使用除留取余法 确定地址 采用开放地址法 解决冲突
//hash表创建 返回值为申请 的 表长
int hash_init(int **hash,int m)
{
int n = m/0.75;
*hash = (int *)malloc(sizeof(n * sizeof(int)));// n/0.75 算出填充因子
memset(*hash,'\0',n*4);
if(hash == NULL)
{
perror("malloc");
exit(-1);
}
return n;
}
//hash表插入数据 *hash
void insert_hash(int hash[],int len,int data)
{
int addr = data % len;//哈希地址
while(hash[addr] != 0)
{
addr = (addr+1)%len;//开放地址法存入
}
hash[addr] = data;//放入数据
}
//哈希表 查找
int find_hash(int hash[],int len,int data)
{
int addr = data % len;
int *p = &hash[addr];//查找终点
while(hash[addr] != data)
{
addr = (addr+1)%len;
if(p == &hash[addr])
{
return -1;
}
}
return addr;
}
int main()
{
///哈希查找
int *hash = NULL;
int m;
printf("请设置hash表的大小: ");
scanf("%d",&m);
int len = hash_init(&hash,m);
int data;
for(int i =0;i< m;i++)
{
printf("inset data: ");
scanf("%d",&data);
insert_hash(hash,len,data);
}
printf("find data: ");
scanf("%d",&data);
printf("find pos %d\n",find_hash(hash,len,data));
//
/*
int a[21] = {7,4,16,1,9,6,3,5,0,8,12,14,18,2,13,19,17,11,15,10,14};
int len = sizeof(a)/sizeof(a[0]);
int i;
for(i = 0; i < len;i++)
{
printf("%2d ",a[i]);
}
putchar(10);
maopao(a,len);
for(i = 0; i < len;i++)
{
printf("%2d ",a[i]);
}
putchar(10);
//sort(a,len);
//quick_sort(a,0,len-1);
//insert_sort2(a,len);
//shell_sort(a,len);
int data;
printf("查找的数: ");
scanf("%d",&data);
printf("find data pos : %d\n",binarySearch1(a,len,data));
printf("find data pos : %d\n",binarySearch(a,len,0,len-1,data));
*/
return 0;
}