排序Sort
排序算法比较
排序 | 稳定性 | 最坏 | 最好 | 平均 |
---|---|---|---|---|
冒泡 | √ | n^2 | n | n^2 |
插入 | √ | n^2 | n | n^2 |
选择 | × | n^2 | n^2 | n^2 |
希尔 | × | n^2 | n^1.5 | n^1.3 |
快速 | × | n^2 | n logn | n logn |
归并 | √ | n logn | n logn | n logn |
堆 | × | n logn | n logn | n logn |
基数 | √ | d(n+r) | d(n+r) | d(n+r) |
冒泡排序
重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成
实现:
public static void bubbleSort(int[] arr) {
//如果为空或者只有一个数据 则直接返回
if (arr.length == 0 || arr.length == 1) {
return;
}
//数组长度
int length = arr.length;
//用于中间的转换
int temp = 0;
//使用双重for循环 只用比较length-1次
//外面的循环 记录比较的次数
for (int i = 1; i < length; i++) {
boolean flag = false;//如果其中一次没有交换 则证明直接就是有序的
for (int j = 0; j < length - i; j++) {
if (arr[j] > arr[j + 1]) {
flag = true;
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (!flag) {
break;
}
}
}
插入排序
简单来说,就是将要排序的元素,分为两部分,一部分为有序表,一部分为无序表,每次从无序表中取出一个元素插入到有序表中。
刚开始第一个元素我们认为他是有序的,把后面的元素逐个插入到前面的有序队列中
实现:
public static void insertSort(int[] arr) {
if (arr.length == 0 || arr.length == 1) {
return;
}
//默认前面时有序的
for (int i = 1; i < arr.length; i++) {
int value = arr[i];//此处需要记录下来 因为可能会i出的元素会发生改变
int index = i-1;//想给前一个位置插入
while (index>=0 && arr[index]>value){
//将大的往后移动
arr[index+1] = arr[index];
index--;
}
if (index+1!=i){
//如果发生过交换
arr[index+1] = value;
}
}
}
选择排序
是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。选择排序是不稳定的排序方法
实现:
/**
* 选择排序
* @param arr 要排序的数组
*/
public static void selectSort(int[] arr){
if (arr.length==0||arr.length==1){
return;
}
//得到数组的长度
int length = arr.length;
//外部for循环控制循环的次数
for (int i =0;i<length;i++){
int min = i; //最小的下标
for (int j = i +1;j<length;j++){
//得到后面最小的数的下标
if (arr[j]<arr[min]){
min = j;
}
}
//交换两个元素
if (min!=i){
int temp = arr[i];
arr[i] =arr[min];
arr[min] = temp;
}
}
}
快速排序
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都小于或者等于另外一部分的所有数据,然后再按照此方法对这两部分数据分别进行快速排序,整个排序的过程可以递归进行,以此达到整个数据变成有序序列
代码实现:
/**
* 快速排序
*
* @param arr 要排序的数组
* @param start 左边界
* @param end 有边界
*/
public static void quickSort(int[] arr, int start, int end) {
if (arr.length == 0 || arr.length == 1) {
return;
}
int l = start;
int r = end;
int base = arr[l];//这个数作为基准
while (l < r) {
while (arr[l]<base){
//找到一个大于等于基准的值 将来替换到右边
l++;
}
while (arr[r]>base){
//找到一个小于等于基准的值 将来替换到左边
r--;
}
if (l>=r){
//如果l>=r 说明已经符合一次排序了
break;
}
//否则进行交换
int temp = arr[l];
arr[l] = arr[r];
arr[r]=temp;
if (arr[l]==base){
//此处时防止两个同时为基准值 则会一直死循环
r--;
}
}
if (l==r){
l++;
r--;
}
//对左右两边继续进行快速排序
if (start<r){
quickSort(arr, 0,r );
}
if (l<end){
quickSort(arr, l, end);
}
}
希尔排序
希尔排序是记录按下标的一定增量分组,对分组使用直接插入排序)算法排序;随着增量分组逐渐减少,每组包含的关键词越来越多。当增量分组减至1时,整个文件恰被分为一组,算法便终止。
方法的实质其实时分组插入排序算法,就是对每个每组进行插入排序
public static void shellSort(int[] arr) {
if (arr.length!=0) {
//用来存储临时变量
int temp;
//首先分为n/2组,即每2个为一组。则每组元素之间的间隔为n/2。用step来表示步长,即同组之间的间隔。
for (int step = arr.length / 2; step > 0; step /= 2) {
//分为了n/2组,则前n/2个元素都是不同组的元素,默认第一个元素都是有序的,从每组的第二个数进行插入排序。跟同组前面有序的进行比较
//直到遍历到最后一个数
for (int i = step; i < arr.length; i++) {
//每次跟前面同组的元素进行比较
for (int j = i - step; j >= 0; j -= step) {
if (arr[j] > arr[j + step]) {
temp = arr[j];
arr[j] = arr[j + step];
arr[j + step] = temp;
}
}
}
}
}
}
归并排序
归并排序是利用归并的思想实现的排序方法,该算法采用经典的分治策略,将问题分成小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案修补在一起,即分而治之。
public class MergeSort {
public static void mergeSort(int[] arr, int left, int right) {
//数组的中轴
int mid = (left + right) / 2;
//如果分的数组的长度大于1
if (left < right) {
//向左递归排序
mergeSort(arr, left, mid);
//向右递归排序
mergeSort(arr, mid + 1, right);
//排序
merge(arr, left, right); //合并 排序的过程
}
}
public static void merge(int[] arr, int left, int right) {
int[] temp = new int[arr.length]; //建立一个等长的数组用来存储有序的数组
//中轴
int mid = (left + right) / 2;
int l = left;//左边序列的起时
int r = mid + 1; //右边序列的起时
int index = 0;
//将有序的值存储到临时数组中 直到左边或者右边存储完
while (l <= mid && r <= right) {
if (arr[l] <= arr[r]) {
temp[index] = arr[l];
l++;
index++;
} else {
temp[index] = arr[r];
index++;
r++;
}
}
//将没存储玩的另一边存储到临时数组
while (l <= mid) {
temp[index] = arr[l];
l++;
index++;
}
while (r <= right) {
temp[index] = arr[r];
index++;
r++;
}
index = 0;
int tempLeft = left;
//将有序数拷贝到原来的数组中
while (tempLeft <= right) {
arr[tempLeft] = temp[index];
index++;
tempLeft++;
}
// System.out.println(Arrays.toString(arr));
}
}
基数排序
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlogm),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法
public static void radixSort(int[] arr) {
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//记录最大的位数。
int maxLength = (max + "").length(); //记录位数
//System.out.println(maxLength);
//定义一个二维数组,长度为10,代表0-9,每个数组的长度为arr.length,
int[][] bucket = new int[10][arr.length];
//定义一个数组,用来存储每个数组实际存储数字的个数;
int[] bucketSize = new int[10];
//将数据装入数组中
// i 代表的是位数 先比较个位 然后是十位 .....
for (int i = 0,n=1; i < maxLength; i++,n*=10) {
//对数组中的元素进行处理
for (int value : arr) {
//得到每位代表的数字
int num = value / n % 10;
//存入桶中
bucket[num][bucketSize[num]] = value;
bucketSize[num]++;
}
int index = 0;
//重新放入数组中
for (int k = 0; k < 10; k++) {
if (bucketSize[k] != 0) {
for (int j = 0; j < bucketSize[k]; j++) {
arr[index] = bucket[k][j];
index++;
}
bucketSize[k] = 0;
}
}
}
//用来给arr中存排序的数据
}