目录
一、回顾上次排序内容
冒泡排序 每次冒出一个
选择排序 每次选出一个放到合适位置
插入排序 把左边一个元素看成一个有序数组从第二个元素起把元素一个个的插入到有序数组中
希尔排序 按步长 分组 组内作插入排序
基数排序 数组下标天然有序 注意数组下标的限制:1.空间 2.正整数 3.不重复
箱排序 分成若干个箱子,箱子内作其他排序
二、分治应用之二分查找
1.背景:
有一个文件,文件中存有2G个电话号码。从中查找。
1. 内存操作比文件操作效率高很多。
2. 先排序
假设有1-n的数字,最快猜多少次猜到这个数字。
tips:结果会告知你
log2N
2.mid = l + (r-l)/2
注:若用mid=(l+r)/2容易越界(65535or larger)
3.循环,非递归版本->二分查找实现代码
int BinarySearch(int arr[], int len,int posData)
{
int left = 0,right=len-1;
int mid = left + (right - left) / 2;//不推荐left+right 容易超出整数范围
while (left <= right)
{
mid = left + (right - left) / 2;
if (arr[mid] == posData)return mid;
else if(arr[mid]>posData){right = mid - 1;}//更新搜索范围
else { left = mid + 1; }
}
return -1;
}
4.递归版本->二分查找实现代码
/*2.递归版本->二分查找*/
int BinarySearch(int arr[], int left,int right,int posData)
{
if (left > right)return -1;//递归终止条件☆
int mid = left + (right - left) / 2;
if (arr[mid] == posData)return mid;
else if (arr[mid] > posData) return BinarySearch(arr, left, mid - 1, posData);
else return BinarySearch(arr, mid + 1, right, posData);
}
三、分治应用之归并排序
1.思想:
把两个有序数组合并成一个有序数组
2.步骤:
①创建临时数组
② 一个个比较把两个有序数组中的元素逐个放到临时数组中
③ 把剩下的放到临时数组中
④ 临时数组覆盖待排序数组 (注:memcpy效率更高
注意:执行顺序从下面的递归代码也可以看得出来,优先不断的执行第一个“分”,直到不能再分为止,然后退到上一层分,然后和1 1 2 ->2 2 4 个人感觉来说->有点像二叉树的DFS
3.代码实现:
void MergeSort(int* arr, int left, int right)
{
if (left >= right)return;//相等也没有必要再去分了
int mid = left + (right - left) / 2;
MergeSort(arr, left, mid);//类似于dfs,一直在此分支上搜到底
MergeSort(arr, mid + 1, right);
Merge(arr, left, mid, right);
}
void Merge(int* arr, int left, int mid, int right)
{
int* pArr = new int[right - left + 1];
int k = 0;
int i = left;//左区间的起始
int j = mid + 1;//右区间的起始
//1.依次去比较
while (i <=mid&&j<=right)
{
if (arr[i] < arr[j])
pArr[k++] = arr[i++];
else
pArr[k++] = arr[j++];//从小到大依次进行排序
}
//2.总会有剩下的,依次放入即可
while (i <= mid)pArr[k++] = arr[i++];
while (j <= right)pArr[k++] = arr[j++];
//3.把临时数组的东西拷贝回去
memcpy(arr+left, pArr,sizeof(int)*( right - left + 1));
delete[]pArr;
pArr = nullptr;
}
别忘记剩下的拷贝进去!!!
四、分治应用之快速(分组)排序
1.思想:
把数据分成两组 左边的 比中间值小 右边的比中间值大
2.步骤:(无需swap版)
基本要求:
①递归的终止条件!!
②一个基准点,两个移动指针(保证i的左侧均小于privot,j的右侧均大于privot,当两者相遇之时,privot的位置get√)
③本处采用覆盖的方式,对空间的开销更小,中间很巧妙,第一次j去覆盖i的时候,i的数据实际上已经保存在privot中,下一次是i是以一个大于privot的值去覆盖j,恰好方便j下一次开始循环,同时不用担心丢失数据,因为i去覆盖j的时候,上一次的j的值已经存在i上一次被覆盖的位置了。
④一定注意:谁覆盖谁!!->其实也很好记,下一次是j的循环,那必定是在i的循环中发现了比privot大的数字,所以赋值给j,刚好可以开始下一次的循环
图解示例:
3.代码实现:
/*①快速排序*/
void quick_sort(int* a, int len)
{
Qsort(a, 0, len - 1);
}
void Qsort(int* a, int left, int right)
{
if (left >= right)return; //注意递归终止条件
int i = left;//左移动指针
int j = right;//右移动指针
int privot = a[left];//选取一个基准点
while (i < j)
{//要求i的左侧均要小于privot,j的右侧均要大于privot
while (i < j && a[j] >= privot) j--;//当遇到第一个a[j]是比privot小的时候,覆盖
a[i] = a[j];//->一定注意覆盖的顺序,j发现小的给i位置,刚好也能够方便下次i的循环
while (i < j && a[i] <= privot)i++;
a[j] = a[i];
}//出循环表示i=j相遇了,此时更新相遇点->privot归位
a[i] = privot;
//递归
Qsort(a, left, i - 1);//左边进行排序
Qsort(a, i + 1, right);//右边进行排序
}
五、快排其他写法参照(swap):
这里的两个指针分别是index和i,i走在index前面。
int partition(int array[], int left, int right)
{
int pivot = left;
int index = pivot + 1;
for (int i = index; i <= right; i++)
{
if (array[i] < array[pivot])
{
Swap(array, i, index);
index++;
}
}
Swap(array, pivot, index - 1);
return index - 1;
}
void quickSort(int array[], int left, int right)
{
if (left < right)
{
int index = partition(array, left, right);
quickSort(array, left, index - 1);
quickSort(array, index + 1, right);
}
}
void QuickSort(int array[], int arrayNum)
{
quickSort(array, 0, arrayNum - 1);
}
7.21测试上述两种快排的方式->第一种覆盖的方式会快于交换的方式!!!