10种排序算法的复杂度,比较,与实现

1.时间复杂度和空间复杂度,及稳定性

2.冒泡排序

 最好是n,最坏是 n的平方,在内存里排。是稳定的。

void Bubble_Sort(int[] arr)    //参数为数组和长度
{
    int n=arr.length;
	for (int i = 0; i < n ; i++) {
		for (int j = 0; j < n - i - 1; j++) {  //n-i-1是比较次数,每比较一趟就少一次
			if (arr[j] > arr[j + 1]) {        //交换arr[j]与arr[j+1]的值
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

3.插入排序

 public void insertSort(int [] nums)
    {
        if(nums.length==0 && nums.length==1)
        {
            return ;
        }

//注意,遍历的最大位置是 长度减2
    for(int i=0;i<nums.length-1;i++)
    {
        //这个border,表示已排数组的右界。
        int border=i;
        int current =nums[i+1];
        
        //从右往左遍历已排数组,如果比current大,就让当前遍历的这个元素后移一位。
        while(border>=0 && current<nums[border])
        {
            //右移一位
            nums[border+1]=nums[border];
            border--;
        }
        //停的时候,说明已经找到 那个小于等于 current的元素的位置了。
        nums[border+1]=current;

    }

    }

4.希尔排序(里面用了直接插入排序)

最佳情况:T(n) = O(n)

最坏情况:T(n) = O(n2)

平均情况:T(n) = O(n2)           

不稳定: 需要多次插入排序,值相同的元素可能在各自的插入排序中移动,最后其 稳定 性就会被打乱

 算法停止的标识是 gap=0。其实就是每次移动gap个单位的,直接插入排序。

public void HillSort(int [] nums)
 {
     int gap =(nums.length)/2;
    //希尔排序的循环条件是gap>0
    while(gap>0)
    {
        //不同于直接插入排序,这里最大索引到nums.length-1
        for(int i=gap;i<nums.length;i++)
         {
             //i-gap,就是直接插入排序的右界。
             int border=i-gap;

             //
             int current=nums[i];
             //这个while,就是一个一次移动gap位置的直接插入排序。
             while(border>=0 && nums[border] >current )
             {
                 //往后移动gap个位置
                 nums[border+gap]=nums[border];
    
                 border-=gap;   
             }
             //出来了,说明碰到小于等于current的了。
             nums[border+gap]=current;

         }
         gap=gap/2;
    }
 }

5.归并排序

最佳情况:T(n) = O(nlog2 n)

最坏情况:T(n) = O(nlog2 n)

平均情况:T(n) =O(nlog2n)       稳定

递归的把一个数组拆分为两个数组,直接数组长度只剩1(也就是分成两个元素)。

就是不断地递归  有序地合并 拆分的两个子数组。

合并的方法就是,有序合并 两个数组,且让新数组有序。

那为什么要递归到最深,也就是两个数组只有一个元素的时候呢?

因为这样复杂度低。如果只整一层归并呢?那样复杂度就是n的平方  即(n/2)(n/2)。

public int[] mergeSort(int[] arr)
{
    //当数组大小为1的时候,直接return,让递归的上一层排序即可。
    if (arr.length == 1) return arr;
   //copyOfRange,左闭右开
    int mid= (arr.length)/2;
    int [] leftArr=Arrays.copyOfRange(arr,0,mid);
    int [] rightArr=Arrays.copyOfRange(arr,mid,arr.length);

    //mergeSort 必须要有返回值,因为上一层要用
   return merge(mergeSort(leftArr),mergeSort(rightArr));
   
}
  
// 就是把两个有序数组, 合并成一个有序数组。
public  int[] merge(int[] left, int[] right) {
  int[] result = new int[left.length + right.length];
  for (int index = 0, i = 0, j = 0; index < result.length; index++) {

//i 是用来遍历left 数组的,i都大于等于left.length了,则说明 left数组的元素都加完了
    if (i >= left.length)
      result[index] = right[j++];
    else if (j >= right.length)
      result[index] = left[i++];
    else if (left[i] > right[j])
      result[index] = right[j++];
    else
      result[index] = left[i++];
  }

  return result;
}

6.选择排序

最佳情况:T(n) = O(n) 最差情况:T(n) = O(n2) 平均情况:T(n) = O(n2)

不稳定

就是每一轮都把最小的值放在最前面。

 比如第一轮 确定 1为最小的,放在最前面。 下一轮就不用 考虑第一个位置。

第一次排序结果:{1,3,7,2,6,9,5,8}

第二轮就 ,确认2是 第二轮最小的。下一轮就不用 考虑前两个位置。

第二轮排序结果为:{1,2,7,3,6,9,5,8}。

public static int[] selectionSort(int[] array) {
  if (array.length == 0)
    return array;
  for (int i = 0; i < array.length; i++) {
    

    //每个i就是一轮。
    int minIndex = i;//最小索引初始化为。
    for (int j = i; j < array.length; j++) {
      if (array[j] < array[minIndex]) //找到最小的数
        minIndex = j; //将最小数的索引保存
    }
    //然后 位置i 和最小的索引 minIndex 的值交换。
    //i 位置就成了这一轮最小的。
    int temp = array[minIndex];
    array[minIndex] = array[i];
    array[i] = temp;
  }
  return array;
}

7.快速排序

 时间复杂度:

  • 最佳情况:T(n) = O(nlogn)
  • 最差情况:T(n) = O(n的平方)
  • 平均情况:T(n) = O(nlogn)

空间复杂度(因为每次都要记录基准值):O(logn)

但是排序不稳定。

给定一个序列   6 1 2 7 9 3 4 5 10 8

在初始状态下,数字6在序列的第1位。我们的目标是将6挪到序列中间的某个位置,假设这个位置是k。现在就需要寻找这个k,并且以第k位为分界点,左边的数都小于等于6,右边的数都大于等于6。想一想,你有办法可以做到这点吗?

变成如下这样   6 1 2 7 9 3 4 5 10 8。

做法:

  1. 两个指针 一个从 右往左,一个从左到右。i=left ,j=right。i从基准 开始。
  2. 基准在左,就先 左移右指针。直到碰到一个小于 基准的元素。然后  i ,j位置元素交换。  然后再右动左指针,让直到碰到一个大于 基准的元素,然后  i ,j位置元素交换。
  3. 当  i 和 j相等的时候,就是 这一轮 的结束。
  4. 这种i和 j交叉 行走的方式, i 和 j最后一定会停在同一个位置。
  5. 最终把temp,插入到位置 i即可。
  6. 然后递归处理 最终位置 两侧的子数组即可。

  void quickSort(int[] arr,int left,int right)
   {
       if (right >= left) {
		//保存基数
		int basic = arr[left];
		//定义左右指针
		int i = left;
		int j = right;
		while (i < j) {		//左指针小于右指针
			while (i < j && arr[j] > basic) {//操作右指针找到小于基数的下标
				j--;
			}
            //出了这个while,立马就得判断
			if (i < j) {
				arr[i] = arr[j];	
				i++;				
			}
			while (i < j && arr[i] < basic) {//相反,找到大于基数的下标
				i++;
			}
			if (i < j) {
				arr[j] = arr[i];	//大于基数的值赋给右指针所指的位置
				j--;				//右指针左移
			}
		}
		arr[i] = basic;				//将基数放入到指针重合处
		quickSort(arr, left, i - 1);	//递归调用,对左半部分数组进行排序
		quickSort(arr, i + 1, right);	//对右半部分数组进行排序
	}

   }

8. 堆排序

堆 是一个完全二叉树。

大根堆:每个节点的值都大于或者等于他的左右孩子节点的值。

跟二叉排序树不同之处是,  孩子节点只需要 小于父亲节点即可。

它满足完全二叉树的一些规则,最后一个非叶子节点是 arrLengrh/2-1

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
比较直接插入排序、希尔排序、直接选择排序、堆排序、起泡排序、快速排序、归并排序、基数排序。随机生成一组待排序数据,个数不少于100个;各算法对同一数据排序所需要的关键字比较次数和关键字移动次数,至少使用5组数据进行比较。1)插入排序:每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。 2)冒泡排序:两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。 3)选择排序:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。 4)Shell排序:在直接插入排序算法中,每次插入一个数,使有序序列只增加1个节点,并且对插入下一个数没有提供任何帮助。如果比较相隔较远距离(称为 增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。 5)归并排序:设有两个有序(升序)序列存储在同一数组中相邻的位置上,不妨设为A[l..m],A[m+1..h],将它们归并为一个有序数列,并存储在A[l..h]。 6)快速排序:快速排序是对冒泡排序的一本质改进。它的基本思想是通过一趟扫描后,使得排序序列的长度能大幅度地减少。在冒泡排序中,一次扫描只能确保最大数值的数移到正确位置,而待排序序列的长度可能只减少1。快速排序通过一趟扫描,就能确保某个数(以它为基准点吧)的左边各数都比它小,右边各数都比它大。然后又用同样的方法处理它左右两边的数,直到基准点的左右只有一个元素为止。 7)堆排序:堆排序是一树形选择排序,在排序过程中,将A[n]看成是完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。 8)基数排序:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
时间复杂度最低的排序算法是计数排序。计数排序是一比较排序算法,它通过统计每个元素的出现次数,然后根据元素的值确定元素在排序后的序列中的位置。由于不需要比较元素的大小,因此计数排序的时间复杂度为O(n+k),其中n是元素的个数,k是元素的取值范围。相比于其他比较排序算法的O(nlogn)时间复杂度,计数排序的时间复杂度更低。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [java_时间复杂度较小的排序方法_时间复杂度O(N)](https://blog.csdn.net/qq_53261858/article/details/123142412)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [比较常用的排序算法](https://download.csdn.net/download/zj1043360940/7578343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [排序的最低时间复杂度为什么是O(nlogn)](https://blog.csdn.net/micx0124/article/details/9852289)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值