常见的排序的特点比较
相关概念
稳 定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不 稳 定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。
冒泡排序
算法描述:
比较相邻的元素。如果第一个比第二个大,就交换它们两个;
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
针对所有的元素重复以上的步骤,除了最后一个;
重复步骤1~3,直到排序完成。
代码实现分析
1)取最左侧的下标为0 的值和后边的值进行比较,如果符合规则,不执行交换,下标右移一位,如果不符合规则,则交换两个数,之后下标右移
2)双层循环嵌套,外层循环控制轮数,一共需要执行num.length-1次
3)内层循环控制每一轮循环的次数num.length-i-1
代码实现
public class BubbleSort {
public static void main(String[] args) {
int[] num = new int[] { 5, 5, 45, 33, 57, 99, 1 };
sort(num);
System.out.println(Arrays.toString(num));
}
//排序的方法
public static void sort(int[] num) {
int temp = 0;
for (int i = 0; i < num.length - 1; i++) {//外层循环num.leng-1
for (int j = 0; j < num.length - i - 1; j++) {//内层循环num.length-i-1
if (num[j] > num[j + 1]) {
temp = num[j + 1];
num[j + 1] = num[j];
num[j] = temp;
}
}
}
}
}
2、选择排序(Selection Sort)
选择排序(Selection-sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
2.1 算法描述
先将最小的值放在最左边,之后找出第二小的数字放在前一个的后边 依次向后执行
每次从数组的前边取出一个数字做为标记数字,之后和后边的顺序进行比较,出现比标志数字小的数字的时候,将此数字标记为最小的数字,继续想后比较,知道最后
这样找出最小的数字的下标了,将初始的第一个数字和最小值的数字进行交换,
public class SelectSort {
public static void main(String[] args) {
int[] num = new int[] { 44, 33, 22, 55, 44, 33, 1, 5, 88, 45, 66, 2, 88, 555, 49 };
SelectSort(num);
System.out.println(Arrays.toString(num));
}
public static void SelectSort(int[] num) {
int temp;
// 一共需要进行num.length-1次循环
for (int i = 0; i < num.length - 1; i++) {
// 定义每一轮最小值的初始化的下标
int minIndex = i;
// 从这个元素开始向后边遍历,出现比最小值小的数字之后,将这个值的下标赋给最小值下标
for (int j = i + 1; j <= num.length - 1; j++) {
if (num[minIndex] > num[j]) {
minIndex = j;
}
}
// 此轮比较结束,此时minIndex中记录的就是最小值的下标,和初始值进行交换
temp = num[i];
num[i] = num[minIndex];
num[minIndex] = temp;
}
}
}
3、插入排序(Insertion Sort)
插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
3.1 算法描述
一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:
从第一个元素开始,该元素可以认为已经被排序;
取出下一个元素,在已经排序的元素序列中从后向前扫描;
如果该元素(已排序)大于新元素,将该元素移到下一位置;
重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
将新元素插入到该位置后;
重复步骤2~5。
3.2 动图演示
/***********************************************
*插入排序:
*有一个已经有序的数据序列,要求在这个已经排好的数据序列中插入一个数,
*但要求插入后此数据序列仍然有序,这个时候就要用到一种新的排序方法——插入排序法
*用插入排序对无序数组进行排序:
*最左边的一个元素可认为是有序的,
*取第二个元素作为标志位和前边的元素进行比较,如果出现比自己大的值就将其向后移动一位,
*出现小于该值的数的话就将标志位放在这个数的后边,
*进行下一轮
***********************************************/
public class InsertSort {
public static void main(String[] args) {
int[] num=new int[] {44,33,22,55,44,33,1,5,88,45,66,2,88,555,49};
Insert(num);
System.out.println(Arrays.toString(num));
}
public static void Insert(int[] num) {
//一共需要进行num.length-1次循环
int temp;
for(int i=1;i<=num.length-1;i++) {
temp=num[i];
//定义向前比较的下标
int preIndex=i-1;
while(preIndex>=0&&num[preIndex]>temp) {//用whikle循环,因为无法判断循环的次数
num[preIndex+1]=num[preIndex];
preIndex--;
}
//从while循环退出来说明遇到了小于等于temp的值了,这时候将temp放在这里
num[preIndex+1]=temp;
}
}
}
4、归并排序(Merge Sort)
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
4.1 算法描述
把长度为n的输入序列分成两个长度为n/2的子序列;
对这两个子序列分别采用归并排序;
将两个排序好的子序列合并成一个最终的排序序列。
代码实现
public class MergeSort02 {
public static void main(String[] args) {
int[] num=new int[] {44,33,22,55,44,33,1,5,88,45,66,2,88,555,49};
chai(num,0,num.length-1);
System.out.println(Arrays.toString(num));
}
public static void chai(int[] num ,int left,int right) {
//退出条件
if(left>=right) {
return;
}else {
int mid=(left+right)/2;
chai(num,left,mid);
chai(num,mid+1,right);
//合并
merge(num,left,mid,right);
}
}
public static void merge(int[] num, int left, int mid, int right) {
//相当于以mid下标的分开的两个有序的数组进行归并排序
//定义一个新的数组接收排序后的数据
int[] new_num=new int[num.length];
//定义两个变量记录两个待排序数组的索引的位置移动
int n=left;
int m=mid+1;
//定义一个变量记录新数组的小标的移动,注意这个下标不能指定数字,应该指定left,因为
//两个待排序数组的起始的下标就是归并的时候第一个需要放入新数组的下标
int index=left;
while(n<=mid&&m<=right) {
if(num[n]<num[m]) {
//将大的值放入新的数组中
new_num[index++]=num[n++];
}else {
new_num[index++]=num[m++];
}
}
//跳出while循环之后说明归并的时候有一个原始数组取完元素了,但是另外一个数组肯定
//还有数据,因为原始数组是有序的数组,所以讲另外的数组的剩余的部分直接放入到新的数组中去
while(n<=mid) {
new_num[index++]=num[n++];
}
while(m<=right) {
new_num[index++]=num[m++];
}
/**
*到这里原始数组中left--right之间的数据已经有序的放入了新的数组中
*但是每次调用归并方法的时候都是传入的原始数组,所以应该将排好序的数组中的left---right之间
*的内容重新写入到原始的数组中去,这样每次排好原始数组中的left--right之间的元素
*
*/
for(int i=left;i<=right;i++) {
num[i]=new_num[i];
}
}
}
5、快速排序(Quick Sort)
快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。
5.1 算法描述
快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
代码实现
public class QuickSort {
public static void main(String[] args) {
int[] num=new int[] {5,5,45,33,57,99,1};
QuickSort qs=new QuickSort();
qs.Sort(num, 0, num.length-1);
System.out.println(Arrays.toString(num));
}
public void Sort(int[] num ,int left,int right) {
//退出递归的条件
if(left>right) {
return;
}
//定义一个方法获得分界线
int index=getIndex(num,left,right);
//左边进行递归
Sort(num,left,index-1);
//右边进行递归
Sort(num,index+1,right);
}
public int getIndex(int[] num, int left, int right) {
//的定义一个标志值key
int key=num[left];
while(left<right) {
//从右边向左边进行比较,如果发现比key小的值,左右两个值交换位置
//否则向左移动遍历,直到left=right
while(left<right&&num[right]>=key) {
right--;
}
num[left]=num[right];
num[right]=key;
//之后从左边向右边进行遍历,如果出现比key大的值的话,左右交换位置
while(left<right&&num[left]<=key) {
left++;
}
num[right]=num[left];
num[left]=key;
}
return left;
}
}
6、计数排序(Counting Sort)
计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。
6.1 算法描述
找出待排序的数组中最大和最小的元素;
统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
代码实现
/***********************************************
*计数排序:
*思路,
*1)寻找数组最大值max
*2)创建新数组,长度max+1
*3)遍历数组,将数组内容放在新数组该内容的小标上,出现重复的话在新数组元素进行累加
*也就是如果需要排序数组中有2个3的话就把新数组[3]的内容写为2
*4)遍历输出新数组
***********************************************/
public class CountSort {
public static void main(String[] args) {
int[] num=new int[] {44,33,22,55,44,33,1,5,88,45,66,2,88,555,49};
int[] countSort = CountSort(num);
System.out.println(Arrays.toString(countSort));
}
public static int[] CountSort(int[] num) {
//求出数组的最大值
int max=num[0];
for (int n : num) {
if(n>max) {
max=n;
}
}
//创建新的数组
int[] c_num=new int[max+1];
//遍历数组,将数组的内容加入新数组该内容下标
for (int i : num) {
c_num[i]++;
}
//将排序后的结果进行重写到原数组中
//定义num数组的下标
int index_num=0;
// 遍历新数组
for(int i=0;i<c_num.length;i++) {
//如果新数组对应下标元素大于0说明这个小标的值在原数组中有一个或者多个,将他遍历出来放在原数组
while(c_num[i]>0) {
num[index_num++]=i;
c_num[i]--;
}
}
return num;
}
}