需要手写的:冒泡排序 快速排序
理解内容的:堆排序 归并排序
一.普通排序
//简单排序
void ChooseSort(int k[], int n)
{
int i, j, temp;
for(i = 0; i < n - 1; i++)
{
for(j = i + 1; j < n; j++)
{
if(k[i] > k[j])
{
temp = k[i];
k[i] = k[j];
k[j] = temp;
}
}
}
}
二.冒泡排序
C
若出现只比较没有移动,则表明后面的数字已经排好的顺序,即可以退出循环,因而可以改进冒泡排序,加一个标志变量flag去判断是否有移位
//冒泡排序(改进后加一个flag)
void BubbleSort(int k[], int n)
{
int i, j, temp, flag = 1;
for(i = 0; i < n - 1 && flag; i++)
{
for(j = n - 1; j > i; j--)
{
flag = 0;
if(k[j - 1] > k[j])
{
temp = k[j];
k[j] = k[j - 1];
k[j - 1] = temp;
flag = 1;
}
}
}
}
Java
//冒泡排序
for(int i = 0 ; i < arr.length - 1; i++){
for(int j = 0; j < arr.length - 1 - i; j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
三.选择排序
先比较,找出min,再交换
//选择排序
void SelectSort(int k[], int n)
{
int i, j, temp, min;
for(i = 0; i < n; i++)
{
min = i;
for(j = i + 1; j < n; j++)
{
if(k[j] < k[min])
{
min = j;
}
}
if(min != i)
{
temp = k[i];
k[i] = k[min];
k[min] = temp;
}
}
}
四.插入排序
基本方法:每步将一个待排序的元素,按其排序码大小插入到前面已经排好序的一组元素的适当位置上去,直到元素全部插入为止。
分类
可以选择不同的方法在已经排好序的有序数据表中寻找插入位置,依据查找方法的不同,分为常用的三种
①直接插入排序
②折半插入排序
③希尔排序
1.直接插入排序
(在基本有序或者小规模数据的情况下效率很高)
待排序序列的第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
//直接插入排序
void InsertSort(int k[], int n)
{
int i, j, temp;
for(i = 1; i < n; i++)
{
if(k[i] < k[i - 1])
{
temp = k[i];
for(j = i - 1; k[j] > temp; j--)
{
k[j + 1] = k[j]; //往后移
}
k[j + 1] = temp;
}
}
}
2.折半插入排序
将比较和移动操作分离开来,即先折半查找出元素的待插入位置,然后再同意地移动待插入位置之后的所有元素。
//折半插入排序
void BinaryInsertSort(int k[], int n)
{
int i, j, temp, low, high, mid;
for(i = 1; i < n; i++)
{
temp = k[i];
low = 0;
high = i - 1;
while(low <= high)
{
mid = (low + high) / 2;
if(k[mid] > temp)
{
high = mid - 1;
}
else
{
low = mid + 1;
}
}
for(j = i - 1; j >= high + 1; j--)
{
k[j + 1] = k[j];
}
k[j + 1] = temp;
}
}
3.希尔排序
思想是使用一个不断缩小的增量gap将数组元素分组,在每个分组内部先进行插入排序,当gap减少到1时整个数组元素分在一组,最后进行一次插入排序,整个排序过程结束。(但是算法不稳定)
do……while控制gap每次的缩小,其内部便是直接插入排序算法的使用,与直接插入排序算法稍有不同的一点是:每次的变化量是gap而不是1。
gap = gap / 3 + 1;
//希尔排序
void ShellSort(int k[], int n)
{
int i, j, temp;
int gap = n;
do{
gap = gap / 3 + 1;
for(i = gap; i < n; i++)
{
if(k[i] < k[i - gap])
{
temp = k[i];
for(j = i - gap; k[j] > temp; j -= gap)
{
k[j + gap] = k[j];
}
k[j + gap] = temp;
}
}
}while(gap > 1);
}
五 .堆排序
从小到大构造大顶堆,从大到小构造小顶堆
将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点。将它移走(其实就是将其与堆数组的末尾元素交换,此时末尾元素就是最大值),然后将剩余的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次最大值。如此反复执行,就能得到一个有序序列了。这个过程其实就是先构建一个最大/最小二叉堆,然后不停的取出最大/最小元素插入到新的队列中,以此达到排序的目的。
例如
int main()
{
int i;
int a[10] = {-1, 5, 2, 6, 0, 3, 9, 1, 7, 4}; //从1开始
HeapSort(a, 9);
printf("排序后的结果是:");
for(i = 1; i < 10; i++)
{
printf("%2d", a[i]);
}
return 0;
}
void HeapSort(int k[], int n)
{
int i;
for(i = n / 2; i > 0; i--) //从左到右 从下到上
{
HeapAdjust(k, i, n);
}
for(i = n; i > 1; i--)
{
swap(k, 1, i); //将第一个元素和最后一个互换
HeapAdjust(k, 1, i - 1); //重新调整堆的序列(相当于最大的那个数固定在后面)
}
}
2.交换函数
void swap(int k[], int i, int j)
{
int temp;
temp = k[i];
k[i] = k[j];
k[j] = temp;
}
3.构造堆
这个循环要确保每个结点都符合大顶堆的特点
void HeapAdjust(int k[], int s, int n)
{
int i, temp;
temp = k[s]; //当前需要调整的双亲结点
for(i = 2 * s; i <= n; i *= 2) // 2 * i是双亲的左孩子, 2 * i + 1是双亲的右孩子
{
if(k[i] < k[i + 1] && i < n) //i = n就没有i + 1
{
i++; //i指向最大元素孩子的下标
}
if(temp >= k[i])
{
break;
}
k[s] = k[i]; //大元素放在双亲的位置
s = i;
}
k[s] = temp;
}
六.归并排序
1.递归思想
总的拆成两部分,再拆成两部分,直到不能拆为止
void MergeSort(int k[], int n)
{
if(n > 1)
{
int *list1 = k;
int list1_size = n / 2;
int *list2 = k + n / 2;
int list2_size = n - n / 2;
MergeSort(list1, list1_size);
MergeSort(list2, list2_size);
Merging(list1, list1_size, list2, list2_size);
}
}
归并:把两个部分放在一个临时变量里,最后再覆盖
//实现归并,并把最后的结果存放到list1里
void Merging(int *list1, int list1_size, int *list2, int list2_size)
{
int temp[Maxsize];
int i = 0, j = 0, k = 0, m;
while(i < list1_size && j < list2_size)
{
if(list1[i] < list2[j])
{
temp[k++] = list1[i++];
}
else
{
temp[k++] = list2[j++];
}
}
while(i < list1_size)
{
temp[k++] = list1[i++];
}
while(j < list2_size)
{
temp[k++] = list2[j++];
}
for(m = 0; m < list1_size + list2_size; m++)
{
list1[m] = temp[m];
}
}
2.迭代思想
递归调用自己,效率不高
// 归并排序(迭代)
void MergeSort(int k[], int n)
{
int i, left_min, left_max, right_min, right_max, next = 0;
int *temp = (int *)malloc(n * sizeof(int));
for(i = 1; i < n; i *= 2)
{
for(left_min = 0; left_min < n - i; left_min = right_max)
{
right_min = left_max = left_min + i;
right_max = left_max + i;
if(right_max > n)
{
right_max = n;
}
while(left_min < left_max && right_min < right_max)
{
if(k[left_min] < k[right_min])
{
temp[next++] = k[left_min++];
}
else
{
temp[next++] = k[right_min++];
}
}
while(left_min < left_max)
{
k[--right_min] = k[--left_max];
}
while(next > 0)
{
k[--right_min] = temp[--next];
}
}
}
}
七.快速排序
基本内容是选择一个数作为准基数,然后利用这个准基数将遗传数据分为两个部分,第一部分比这个准基数小,都放在准基数的左边,第二部分都比这个准基数大,放在准基数的右边.
(用递归的思想)
快速排序的优化
1.优化选取基准点
确保基准点是处于数据中的中间位置
先找出三个关键字(可以前中后选择一个,进行比较)选择中间的数作为基准点
2.优化不必要的交换
交换直接改换成赋值即可
//优化后的快速排序
void swap(int k[], int low, int high)
{
int temp;
temp = k[low];
k[low] = k[high];
k[high] = temp;
}
int Partition(int k[], int low, int high)
{
int point;
int mid = low + (high - low) / 2;
//整个交换就是在确保low是中间值
if(k[low] > k[high])
{
swap(k, low, high);
}
if(k[mid] > k[high])
{
swap(k, mid, high);
}
if(k[mid] > k[low])
{
swap(k, mid, low);
}
point = k[low];
while(low < high)
{
while(low < high && k[high] >= point)
{
high--; //大的部分依然在右边
}
k[low] = k[high];
while(low < high && k[low] <= point)
{
low++; //小的部分依然在左边
}
k[high] = k[low];
}
k[low] = point; //最后依然要还原
return low; //返回相遇的下标
}
3.优化小数组的排序方案
就快速排序对于大数据而言,效率很高。但如果是一些小数据就完全没有必要,对于小数组,直接插入排序效率很高。一般分界点是high - low > 7为大数据,则在代码区加上一个判断即可。
4.优化递归操作
void QSort(int k[], int low, int high)
{
int point;
if(low < high)
{
while(low < high)
{
point = Partition(k, low, high);
if(point - low < high - point)
{
QSort(k, low, point - 1);
low = point + 1;
}
else
{
QSort(k, point + 1, high);
high = point - 1;
}
}
}
}
八.总结