常用排序
冒泡排序
冒泡排序算法思想
- 从数组头部开始,不断比较相邻的两个元素的大小,让较大的元素逐渐往后移动(交换两个元素的值),直到数组的末尾。经过第一轮的比较,就可以找到最大的元素,并将它移动到最后一个位置。
- 第一轮结束后,继续第二轮。仍然从数组头部开始比较,让较大的元素逐渐往后移动,直到数组的倒数第二个元素为止。经过第二轮的比较,就可以找到次大的元素,并将它放到倒数第二个位置。
- 以此类推,进行 **n-1(n 为数组长度)**轮“冒泡”后,就可以将所有的元素都排列好。
冒泡排序 c 语言实现
#include <stdio.h>
int main(){
int nums[10] = {
4, 5, 2, 10, 7, 1, 8, 3, 6, 9};
int i, j, temp, isSorted;
//优化算法:最多进行 n-1 轮比较
for(i=0; i<10-1; i++){
isSorted = 1; //假设剩下的元素已经排序好了
for(j=0; j<10-1-i; j++){
if(nums[j] > nums[j+1]){
temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = temp;
isSorted = 0; //一旦需要交换数组元素,就说明剩下的元素没有排序好
}
}
if(isSorted) break; //如果没有发生交换,说明剩下的元素已经排序好了
}
for(i=0; i<10; i++){
printf("%d ", nums[i]);
}
printf("\n");
return 0;
}
插入排序
简单插入排序算法原理
- 从整个待排序列中选出一个元素插入到已经有序的子序列中去,得到一个有序的、元素加一的子序列,直到整个序列的待插入元素为0,则整个序列全部有序。
在实际的算法中,我们经常选择序列的第一个元素作为有序序列(因为一个元素肯定是有序的),我们逐渐将后面的元素插入到前面的有序序列中,直到整个序列有序。
示意图如下:
插入排序代码实现:
#include <stdio.h>
/*
直接插入排序:
直接插入排序就是从待排序列中选出一个元素,插入到已经有序的元素之中,直到所有的元素都插入到有序序列中所有的元素就全部
有序了。
通常的做法就是将第一个元素看做是有序的元素(即待排序列的第一个元素看做是有序序列),然后我们将第二个元素和有序序列(即
第一个元素)作比较,按正确的序列插入到序列中去。然后在将第三个元素和前面有序序列(即整个待排序列的前两个元素)作比较,将第
三个插入到前两个元素中去,使得前三个元素有序。以此类推,直到所有的元素都有序。
*/
void insertSort(int *arr[],int len);
int main(int argc, char *argv[])
{
int arr[5]={
3,89,72,43,1
};
insertSort(arr,5);
int i;
for(i=0;i<5;i++){
printf("%d ",arr[i]);
}
return 0;
}
/*
简单插入排序函数
*/
void insertSort(int *arr[],int len){
int i;
int j;
int temp; //定义一个临时变量,用于交换数据时存储
for(i=1;i<len;i++){
//因为我们要对该待排序列的每一个元素都和前面的已排好序的序列进行插入,所以我们会对序列进行遍历
for(j=0;j<i;j++){
//第二层循环主要用于对已排好序的序列进行扫描,和要插入进来的数据进行逐一比较,然后决定插入到哪里
if(arr[j]>arr[i]){
//从前往后对已排好序的元素和待插入元素进行大小比较,然后直到找到一个元素比被插入元素大,则交换位置
temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
}
}
}
选择排序
选择排序算法原理
- 选择排序是一种简单直观的排序算法。它与冒泡排序很相似,都是比较 n-1 轮,每轮都是比较 n–1–i 次,每轮找出一个最大值或最小值。
- 只不过,冒泡排序是将每轮找出的最值放到最右边,而选择排序是将每轮找出的最值放到最左边。并且在算法上,冒泡排序是将相邻的数进行逐个比较,以从小到大排序为例,只要前面的比后面的大,就互换这两个数,直到最后将最大的数“浮”到最右边,如此依次循环。而选择排序是先保存第一个元素的下标,然后后面所有的数依次与第一个元素相比,如果遇到更小的,则记录更小的那个数的下标,然后后面所有的数都依次与那个更小的数比较,直到最后将最小的数的下标找出来,然后再将这个数放到最左边,即与下标为 0 的数互换。如果最小的数的下标就是 0 那么就不用互换。
- 所以,选择排序算法是先判断最小的数的下标是不是 0,如果不是则说明最小的数不是第一个元素,则将这个数与第一个元素互换位置,这样一轮下来最小的那个数就被找到并放到了最左边。
- 在第二轮同样先保存新序列第二个元素的下标,后面所有的数依次与第二个元素相比较,如果遇到更小的则记录更小的那个数的下标,然后后面所有的数都依次与那个更小的数比较,直到最后又找到一个最小的,此时这个最小的在整个序列中是“第二小”。然后再判断这个数的下标是否等于 1,如果不等于 1 说明“第二小”的那个数不是第二个元素,则将这个数与第二个元素互换位置,这样第二轮下来就找到了“第二小”的那个数,并将它放到了第二个位置。如此循环,直到整个序列实现从小到大排序。
- 如果是从大到小排序,那么就记录大的那个数的下标,每一轮找出一个最大的数放到左边。
选择排序算法实现:
# include <stdio.h>
int main(void)
{
int i, j; //循环变量
int MinIndex; //保存最小的值的下标
int buf; //互换数据时的临时变量
int a[] = {
5, 5, 3, 7, 4, 2, 5, 4, 9, 1, 8, 6};
int n = sizeof(a) / sizeof(a[0]); //存放数组a中元素的个数
for (i=0; i<n-1; ++i) //n个数比较n-1轮
{
MinIndex = i;
for (j=i+1; j<n; ++j) //每轮比较n-1-i次, 找本轮最小数的下标
{
if (a[MinIndex] > a[j])
{
MinIndex = j; //保存小的数的下标
}
}
if (MinIndex != i) /*找到最小数之后如果它的下标不是i则说明它不在最左边, 则互换位置*/
{
buf = a[MinIndex];
a[MinIndex] = a[i];
a[i] = buf;
}
}
printf("最终排序结果为:\n");
for (i=0; i<12; ++i)
{
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
归并排序
归并排序算法原理 :
- (Merge Sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并, 使用中牺牲空间换取时间的算法。
此处来自于转载转载来源:
归并算法:
归并算法的核心步骤为:
- 分解
- 合并
- 由于归并排序不依赖于待排序元素序列的初始输入状态, 每次划分时两个子序列的长度基本一致, 因此归并排序的最好, 最差好平均时间复杂度均为O(n*log2^n) , 是一种稳定的排序算法
- 归并排序的优化,使用插入排序处理小规模的子数组,一般可以将归并排序的运行时间缩短10%~15%
归并算法实现(c语言)
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
//分组归并
void _Merge(int *a, int begin1, int end1, int begin2, int end2, int *tmp)
{
int index = begin1;
int i = begin1, j = begin2;
//注意:当划分的区间足够小时,begin1==end1,begin2==end2
while (i <= end1&&j <= end2){
if (a[i]<=a[j])
tmp[index++] = a[i++];
else
tmp[index++] = a[j++];
}
//将左边元素填充到tmp中
while (i <= end1)
tmp[index++] = a[i++];
//将右边元素填充的tmp中
while (j <= end2)
tmp[index++] = a[j++];
//将tmp中的数据拷贝到原数组对应的序列区间
//注意:end2-begin1+1
memcpy(a + begin1, tmp + begin1, sizeof(int)*(end2 - begin1 + 1));
}
//归并排序
void MergeSort(int *a, int left, int right, int *tmp)
{
if (left >= right)
return;
assert(a);
//mid将数组二分
int mid = left + ((right - left) >> 1);
//左边归并排序,使得左子序列有序
MergeSort(a, left, mid, tmp);
//右边归并排序,使得右子序列有序
MergeSort(a, mid + 1, right, tmp);
//将两个有序子数组合并
_Merge(a, left, mid, mid + 1, right, tmp);
}
//打印数组
void PrintArray(int *a, int len)
{
assert(a);
for (int i = 0; i < len; i++)
printf("%d ", a[i]);
printf("\n");
}
int main()
{
int a[] = {
10, 6, 7, 1, 3, 9, 4, 2 };
int *tmp = (int *)malloc(sizeof(int)*(sizeof(a) / sizeof(int)));
memset(tmp, -1, sizeof(a) / sizeof(int));
MergeSort(a, 0, sizeof(a) / sizeof(int)-1, tmp);
PrintArray(a, sizeof(a) / sizeof(int));
system("pause");
return 0;
}
快速排序
快速排序算法思想:
- 先找到某一个元素的具体位置,把数据就可以分成两部分
- 左边的一般按照上边的方法,也可以找到某一个元素的具体位置,然后又分成两部分
- 依次类推,最终找到第一个元素,用到的是递归的思想
快速排序实现:
#include <stdio.h>
#include <stdlib.h>
int FindPos(int * a,int low,int high)
{
int val = a[low];
while(low < high)
{
while(low < high && a[high] <= val)
--high;
a[low] = a[high];
while(low < high && a[low] <= val)
++low;
a[high] = a[low];
} //终止while循环之后,low和high一定是相等的
a[low] = val;
return low; //low可以改为high,返回的是位置
}
void QuickSort(int * a,int low,int high)
{
int pos;
if(low < high)
{
pos = FindPos(a,low,high);
QuickSort(a,low,pos-1);
QuickSort(a,pos+1,high);
}
}
int main()
{
int a[6] = {
-2,1,0,5,4,3};
int i;
QuickSort(a,0,5); //第二个参数表示第一个元素的下标 第三个参数表示最后一个元素的下标
for(i = 0; i < 6; ++i)
{
printf("%d",a[i]);
}
printf("\n");
return 0;
}