排序算法基础

目录

1.选择排序

2.冒泡排序

3.插入排序

4.希尔排序

5.快速排序

6.归并排序


c/c++与java的代码实现的整体框架是一样的,区别只在于java的swap函数需要传数组的引用,而c/c++需要传元素的地址

1.选择排序

时间复杂度:最好情况O(n)  最坏情况O(n^2)

空间复杂度:O(1)

对于有n个数的无序数组

  • 1.下标[0, n-1]中遍历出最小数的下标minindex和最大数小标maxindex
  • 2.下标为minIndex与下标为0的元素交换 maxindex与n-1交换
  • 3.下标为[2, n-2]的元素继续上面的操作

在一趟遍历中同时遍历出最小值和最大值,分别放在开头和结尾,效率快一倍

java实现:

public static void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
     public static void selectionSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;//数组为空 或者只有一个元素
        }
        int l = 0;
        int r = arr.length - 1;
        while (l < r) {
            int min = l;
            int max = l;
            for (int i = l; i <= r; i++) {//区间为[l, r]中寻找 min 和 max
                if (arr[max] < arr[i]) {
                    max = i;
                } else if (arr[min] > arr[i]) {
                    min = i;
                }
            }
            //此时min 和 max 即为 最小 和 最大 元素的下标了  分别和 l 和 r 下标的元素交换
            swap(arr, min, l);
            if (max == l) {//若max元素下标 == l  上面的swap 会把max元素下标和min交换 就需要if判断一下 把max下标找回
                max = min;
            }
            swap(arr, max, r);
            l++;
            r--;
        }
    }

2.冒泡排序

时间复杂度:O(n^2)

空间复杂度:O(1)

左边大就交换,左边小不动,最后大的都在右边

详情还请参考基于冒泡排序的算法研究

3.插入排序

时间复杂度:O(n^2)

空间复杂度:O(1)

  • 1.排序下标在[0, 0]内的元素,就一个元素,已经有序
  • 2.排序下标在[0, 1]内的元素,大的往后移动,使得[0, 1]内的元素有序
  • 3.排序下标在[0, 2]内的元素,使得[0, 2]范围内的元素有序
  • 每次都往后移动一个元素,小的就往前移动,使得新的下标范围内的元素有序
  • ...
  • 4.排序下标[0, n-1]内的元素..
  • 5.后面没有元素了,整个数组就排好序了

java实现:

public static void swap(int[] arr, int i, int j) {
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}
public static void insertionSort(int[] arr){
    if (arr == null || arr.length < 2) {
        return;
    }
        for (int i = 1; i <arr.length ; i++) {//下标范围 [1, sz-1]   //从第二项开始
            for (int j = i; j >0 ; j--) {  // [1, i]
                if(arr[j] < arr[j-1]){
                    swap(arr,j,j-1);
                }
            }
        }
    }

4.希尔排序

插入排序的改良版本

时间复杂度:O(N^1.3)

空间复杂度:O(1)

原理:

  • 1.选定一个增长量h,按照增长量h作为数据分组的依据,对数组进行分组(每一组两个元素)
  • 2.对分好组的每一组数据进行插入排序
  • 3.减小增长量,一直减小到1,重复第二步

增长了h确定规则和减小规则:

//h的初始化
int h = 1;
while(h < 数组的长度/2){
    h = h*2 + 1;
}
//h的减小规则
h /= 2;

比起传统的插入排序,希尔排序的比较次数和交换次数少了很多

java实现:

public static void shellSort(int[] arr){
    int h = 1;
    //h的初始化
    while( h < arr.length/2){
        h = h*2+1;
    }
    while( h >= 1){
        for (int i = h; i <arr.length ; i++) {
            for (int j = i; j >=h ; j -= h) {
                if(arr[j-h] > arr[j]){
                    swap(arr,j-h,j);
                }
                else{
                    break;
                }
            }
        }
        h /= 2;//h的减小
    }

5.快速排序

时间复杂度:平均O(n*logn) 最坏O(n^2)

原理:分治+双指针

  • 1.确定一个基准数k
  • 2.把待排序的数组分为<=k和>=k两边(k具体在哪一边无所谓,且分界点不一定是x)
  • 3.分别对两组进行递归处理

其中核心的部分就是第二步:分组

1.设置两个指针i 和 j,分别指向左边界和右边界

2.分别让i 和 j 向中间移动,i指针遇到>=k的停下,j指针遇到<=k的停下

3.交换i和j所指的值

4.直到i和j相遇(或者交错),i左侧就是<=k的数  j右侧就是>=k的数

具体代码实现:

java实现:

void QuickSort(int[] arr, int l, int r){
        if(l >= r) return;
        int i = l - 1,j = r+1;
        int x = arr[l];
        while(i < j){
            do i++; while(arr[i] < x);
            do j--; while(arr[j] > x );
            if(i < j){
                swap(arr,i,j);
            }
        }
        QuickSort(arr,l,j);
        QuickSort(arr,j+1,r);
}
void swap(int[] arr,int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

c/c++实现:

void quick_sort(int q[], int l, int r) {
	if (l >= r)	return;//l  和   r  不变的

	int x = q[l], i = l - 1, j = r + 1;
	while (i < j) {
		do i++; while (q[i] < x);// 直到 >= x 停止循环
		do j--; while (q[j] > x);// 直到 <= x
		if (i < j) {
			swap(q[i], q[j]);//注意这里是库里的swap函数
		}
	}

	//递归两边  先递归左边  左边排好序  再递归右边  
	quick_sort(q, l, j),quick_sort(q, j+1, r);
}

边界问题:

当递归区间是[l,j]和[j + 1,r],向下取整基准数x

当递归区间是[l,i - 1]和[i,r],向上取整基准数x

避免:当基准数与递归区间的边界重合,使得下一次递归区间不变,陷入死循环的情况

6.归并排序

时间复杂度:O(n*logn)

原理:分治 + 双指针

  • 1.确定分界点:通常取中间点为分界点
  • 2.递归排序左右两边
  • 3.归并

核心:归并

思路:双指针

c/c++实现:

void merge_sort(int q[], int l, int r)
{
	if (l >= r)	return;

	int mid = l + r >> 1;

	//先递归分组
	merge_sort(q, l, mid), merge_sort(q, mid + 1, r);

	int k = 0, i = l, j = mid + 1;
	while (i <= mid && j <= r)
	{
		if (q[i] <= q[j])	tmp[k++] = q[i++];//小的放左边  
		else  tmp[k++] = q[j++];
	}
	// 跳出循环 要判断是否是某一边先走完
	while (i <= mid)	tmp[k++] = q[i++];
	while (j <= r)		tmp[k++] = q[j++];

	//拷贝到原来的数组
	for (i = l, j = 0; i <= r; i++, j++)	q[i] = tmp[j];
}
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值