常见排序算法

排序算法

冒泡排序

将数组中的元素两两交换比较,每一轮交换都可以将未排序序列中的最大值找出来,放在已排序的序列末尾。
该排序算法的时间复杂度稳定为O(n^2),空间复杂度为O(1)(直接在本数组上操作,不需要额外的存储空间)。
一种改进型的方法是判断某一轮循环中是否发生了元素交换,如果没有那么就表明排序已完成,不必在进行后续的排序。

void sort(int[] nums){
          boolean hasChange = true;
          for(int i=0; i < nums.length - 1 && hasChange; i++){
              hasChange = false;
              for(int j = 0; j < nums.length - 1 -i; j++){
                  if (nums[j] > nums[j + 1]){
                      swap(nums, j, j + 1);
                      hasChange = true;
                  }
              }
          }
      }

插入排序

不断地从未排序的数组中取出元素,插入到已排序的数组中。冒泡排序经过每一轮的排序,数组后端的数是排好序的;插入排序经过每一轮的排序,数组前端的数是排好序的。
插入排序的时间复杂度是O(n^2),空间复杂度是n(1)

void sort1(int[] nums){
        for(int i=1; i < nums.length; i++){
            int current = nums[i];
            for(int j = i -1; j >= 0; j--){
                if (nums[j] < current){
                    nums[j + 1] = nums[j];
                }else{
                    nums[j + 1] = current;
                }
            }
        }
    }

归并排序

采用分治的思想,将一个问题分为多个小问题来求解。
具体地:
1)先将一个数组分成两个子数组;
2)递归地将每个子数组分割成更小的数组,直到数组中的元素个数是一个
3)依次按照递归的顺序返回,不断地合并排好序的子数组,直到把整个数组排好序
归并排序的需要创建一个新的大小为n的数组用于保存合并结果,所以空间复杂度是O(n),时间复杂度是O(nlogn)

//主体代码
void mergeSort(int[] arr, int start, int end) {
    if (start >= end){
        return;
    }
    int mid = (start + end) /2;
    //递归滴分割
    mergeSort(arr, start, mid);
    mergeSort(arr, mid + 1, end);
    //无法在分割后合并
    merge(arr, start, mid, end);
}
//合并代码(思想:双指针法,left代表左子数组的起始位置,
//right代表右子数组的起始位置,k表示合并数组的当前位置)
void merge(int[] arr, int start, int mid, int end) {
    int tmpArr[] = new int[end - start + 1];
    int left = start;
    int right = mid + 1;
    int k = 0;
    //合并(left,mid)和(mid+1, end)两个有序小数组,采用了双指针法
    while(left <= mid && right<= end){
        if (arr[left] <= arr[right]){
            tmpArr[k++] = arr[left++];
        }else {
            tmpArr[k++] = arr[right++];
        }
    }
    while(left <=mid){//只剩余左子数组,将其加入到合并数组
        tmpArr[k++] = arr[left++];
    }
    while(right<=end){//只剩下右子数组,将其加入到合并数组
        tmpArr[k++] = arr[right++];
    }
    //将临时数组中的数据copy到arr中
    for (int m = 0; m < tmpArr.length; m++){
        arr[m + start] = tmpArr[m];
    }
}

快速排序

也是采用了分治思想,
1)选择其中一个元素作为基准数据,
2)将小于基准数据的放在左边,将大于基准数据的放在右边,
3)然后继续对左右两个子数组采用快速排序。当子数组的元素个数为1,也就排好了子数组。
空间复杂度O(logn),平均时间复杂度O(nlogn)

void quitSort(int[] nums, int low, int height) {
    if (low >= height){
        return;
    }
	//分区函数,找到一个标准值,将数组分为左右两部分
    int p = partition(nums, low, height);
    //对左半部分递归调用快速排序
    quitSort(nums, low, p - 1);
    //对右半部分递归调用快速排序
    quitSort(nums, p + 1, height);
}
//分区函数实现
int partition(int[] nums, int low, int height) {
	//随机选择一个座位基准值,将基准值与最有边的数交换,
	//这样num[height]就是基准值
    swap(nums, randRange(low, height), height);
    int i, j;
    for(i = low, j = low; j < height; j++){
    	//小于基准值的放在数组左边
        if (nums[j] < nums[height]){
            swap(nums, i++, j);
        }
    }
    //循环到最后j=height,也就是基准值,将nums[i]与基准值交换
    //这样左边的都是小于基准值的,i右边的都是大于基准值的
    swap(nums, i, j);
    return i;//交换后nums[i]就是基准值,i就是基准值下标,返回该下标。
}

常见排序时间复杂度,空间复杂度,稳定性总结

排序算法时间复杂度时间复杂度稳定性
选择排序O(N^2)O(1)X
冒泡排序O(N^2)O(1)V
插入排序O(N^2)O(1)V
归并排序O(N*logN)O(N)V
快速排序O(N*logN)O(logN)X
堆排序O(N*logN)O(1)X

注:稳定性是指,如果数组中有相同的元素,排序后相对位置不变。比如数组下标2,3上各有一个9元素,排序后下标2,3上的9相对位置没变。

一般排序使用快速排序(在没有稳定性要求时),时间复杂度低,空间复杂度较好。
有稳定性要求时,使用归并排序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值