1. 交换排序
冒泡排序
思想:将数据进行两两比较,共循环 n-1次;每次循环确定一个数的位置,全局有序,如:第一次循环将元素两两比较,确定最大或者最小值放在数组的最后一个位置length-1。第二次循环比较未确定位置的元素(n-1),得出无序中的最值放在数组的倒数第二个位置length-2…
-
时间复杂度:
- 最好时间复杂度:当数据有序时外循环只需一次就可以得出结果。
- 最坏时间复杂度:当数据逆序时,外循环需要循环n-1次。
- 平均时间复杂度:
-
稳定性:稳定。
-
代码:
package 交换排序算法; import java.util.Arrays; //冒泡排序算法 public class BubbleSort { public static void main(String[] args) { int[] arr = new int[]{1,2,3,4,5,7,6}; System.out.println(Arrays.toString(arr)); BubbleSort.bubbleSort(arr); System.out.println(Arrays.toString(arr)); } /** * 冒泡排序 * @param arr */ public static void bubbleSort(int[] arr) { for(int i = 0;i < arr.length-1 ;i++) { for(int j = 0 ;j<arr.length - 1 - i;j++) { if(arr[j] > arr[j+1]) { arr[j] = arr[j] + arr[j+1]; arr[j+1] = arr[j] - arr[j+1]; arr[j] = arr[j] - arr[j+1]; } } } } public static void bubbleSort1(int[] arr) { boolean flag = true; int count = 0; for(int i = 0 ;i<arr.length - 1;i++) {//比较length-1次就可以了 flag = true; for(int j = 0; j < arr.length - 1 - i;j++) {//第一轮确定一个数,第二轮确定两个数..... if(arr[j] > arr[j+1]) { arr[j] = arr[j] + arr[j+1]; arr[j+1] = arr[j]-arr[j+1]; arr[j] = arr[j] - arr[j+1]; flag = false; } } count++; if(flag) {//说明这一趟没有交换,数组已经有序 break; } } System.out.println(count); } }
快速排序
思想:
(1)确定数组中的第一个元素的位置:将第一个元素作为标准stard,
从数组的最后一个元素arr[j]和stard比较,如果值比它大,则j–,继续将从后往前和stard比较,直到找到比stard小的元素arr[j],将arr[j]替换arr[i];
然后从前往后和stard的比较,如果值比它小,则i++,否则退出循环,将arr[i]替换arr[j];当i=j时说明元素已经比较完,stard的位置已经确定,将stard替换arr[i]/arr[j]。
(2)递归计算stard左边的元素。
(3)递归计算stard右边的元素。
-
时间复杂度:
- 最好时间复杂度:
- 最差时间复杂度:
- 平均时间复杂度:
-
稳定行:
-
代码
package 交换排序算法; import java.util.Arrays; public class QuickSort { public static void main(String[] args) { int[] arr = new int[] {4,1,3,5,2,8,6,7}; quickSort(arr, 0, arr.length-1); System.out.println(Arrays.toString(arr)); } /** * 快速排序:一次循环只能确定一个数的位置 * @param arr * @param start * @param end */ public static void quickSort(int[] arr,int start,int end) { if(start < end) {//如果start = end 表示只有一个数,不需要排序 //将数组的第一个元素作为标准数 int stard = arr[start]; //记录需要排序的下标 int low = start; int high = end; //循环找出比标准数大的数和比标准数小的数 while(low < high) { //右边的数字和标准数比较,当右边的数字比标准数小时退出循环 while((arr[high] >= stard) && (low<high)) high--; //让右边比标准数小的数替换左边的数 arr[low] = arr[high]; //左边的数字标准数比较,当左边的数字大于标准数时退出循环 while((arr[low] < stard) && (low<high)) low++; //让左边比标准数大的数替换右边的数 arr[high] = arr[low]; }//当low = high时退出循环,此时这个数已经赋值到其它位置,需要被标准数替换 //已经找到标准数相对于数组的位置,比左边的数大,比右边的数小,但是左、右里面的大小位置不确定 arr[low] = stard; //处理左边的数字 quickSort(arr,start,low-1); //处理右边的数字 quickSort(arr,low+1,end); } } }
2. 选择排序
简单选择排序
-
时间复杂度:
- 最好时间复杂度:
- 最差时间复杂度:
- 平均时间复杂度:
-
稳定行:
-
代码:
package 选择排序; import java.util.Arrays; public class SelectSort { public static void main(String[] args) { int[] arr = new int[] { 4, 5, 2, 3, 1, 4, 6 }; selectSort1(arr); System.out.println(Arrays.toString(arr)); } /** * 选择排序算法1:只要找到比第一个小的都交换 * @param arr */ public static void selectSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { for (int j = i + 1; j < arr.length; j++) { if(arr[j] < arr[i]) { arr[j] = arr[j] + arr[i]; arr[i] = arr[j] - arr[i]; arr[j] = arr[j] - arr[i]; } } } } /** * 简单选择排序 * 选择排序算法2:找到还未排序的元素中最小值的下标,如果下标和未排序的第一个下标 * 不一致则交换 * @param arr */ public static void selectSort1(int[] arr) { for(int i = 0; i < arr.length-1;i++) { int minIndex = i; for(int j = i+1;j<arr.length;j++) { if(arr[j] < arr[minIndex]) { minIndex = j; } } if(minIndex != i) { arr[minIndex] = arr[minIndex] + arr[i]; arr[i] = arr[minIndex] - arr[i]; arr[minIndex] = arr[minIndex] - arr[i]; } } } }
堆排序
思想:
1.构造初始堆:将待排序的元素构造成一个大根堆
- [1] 根据元素顺序构造一个完全二叉树。
- [2] 从最后一个非终节点开始构造一个大跟堆。
2.堆排序:
- [1] 将第一个节点和最后一个交换。
- [2] 重新调整最后一个以外的树,调成为一个大跟堆(第一步)。
-
时间复杂度:
- 最好时间复杂度:
- 最差时间复杂度:
- 平均时间复杂度:
-
稳定性:
-
代码
package 选择排序;
import java.util.Arrays;
//堆排序
public class HeapSort {
public static void main(String[] args) {
int[] arr = new int[] {9,6,8,7,0,1,10,4,2};
heapSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void heapSort(int[] arr) {
//开始位置是最后一个非叶子节点,即最后一个节点的父节点
int start = (arr.length-1)/2;
//调整为大跟堆
for(int i=start;i>=0;i--) {
maxHeap(arr,arr.length,i);
}
//先把数组中的第0个和堆中的最后一个数交换位置,再把前面的处理为大跟堆
for(int i = arr.length-1;i>0;i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
maxHeap(arr,i,0);
}
}
public static void maxHeap(int[] arr,int size,int index) {
//左子节点
int leftNode = 2*index+1;
//右子节点
int rightNode = 2*index+2;
int max = index;
//和两个子节点分别对比,找出最大的节点
if(leftNode<size && arr[leftNode] > arr[max]) {
max = leftNode;
}
if(rightNode<size && arr[rightNode] > arr[max]) {
max = rightNode;
}
//交换
if(max != index) {
int temp = arr[max];
arr[max] = arr[index];
arr[index] = temp;
//交换位置以后,可能会破坏之前的大根堆,所以,之前排好的堆需要重新调 整
maxHeap(arr,size,max);
}
}
}
package _52.数据流中的中位数;
import java.util.Arrays;
/**
* 堆排序:大根堆
* @author Administrator
*
*/
public class HeapSort {
public static void main(String[] args) {
int[] arr = {1,4,3,5,2,6};
maxHeapSort(arr);
System.out.println(Arrays.toString(arr));
}
/**
* 堆排序的入口
* @param arr
*/
public static void maxHeapSort(int arr[]){
//1.从堆的最后一个非叶子结点开始构造一个大根堆
int start = (arr.length-1-1) / 2;//最后一个非叶子结点,下标从0开始的
for(int i = start; i >= 0; i--){
maxShift(i,arr.length - 1,arr);
}
System.out.println(Arrays.toString(arr));
//2.将大根堆中的第一个元素和最后一个元素交换,交换后的最后一个元素就确定了自己的位置
for(int i = arr.length - 1; i > 0; i--){
arr[0] = arr[0] + arr[i];
arr[i] = arr[0] - arr[i];
arr[0] = arr[0] - arr[i];
maxShift(0,i-1,arr);
}
}
/**
* 调整大根堆
* @param low 需要调整的堆的根结点
* @param high 堆的最后一个结点
*/
public static void maxShift(int low,int high,int arr[]){
//需要调整的堆的根结点
int root = low;
//左孩子结点
int left = 2*low+1;
while(left <= high){
//找出左孩子结点和右孩子结点中值最大的一个
if(left < high && arr[left] < arr[left+1])
left++;
//如果孩子结点的最大值比根结点大则交换
if(arr[left] > arr[root]){
arr[root] = arr[root] + arr[left];
arr[left] = arr[root] - arr[left];
arr[root] = arr[root] - arr[left];
//交换后可能会破坏left的大根堆,继续调整
root = left;
left = 2 * root +1;
}
else{
break;
}
}
}
}
3. 插入排序
思想:将元素分为两个部分:有序部分和无序部分。将无序部分的第一个元素和有序部分的最后一个元素开始比较大小,如果无序的元素比有序的元素大,则将该无序的元素插入到该有序元素的后一个数。
直接插入排序
-
时间复杂度:
- 最好时间复杂度:
- 最坏时间复杂度:
- 平均时间复杂度:
-
稳定性:
-
代码:
package 插入排序; import java.util.Arrays; public interface InsertSort { public static void main(String[] args) { int[] arr = new int[] {2,5,3,1,4,7,6}; insertSort(arr); System.out.println(Arrays.toString(arr)); } /** * 直接插入排序 * @param arr */ public static void insertSort(int[] arr) { //对于整个数组 for(int i = 1 ;i<arr.length;i++) {//需要循环的次数&&对于arr[i]是需要插入的元素 //将数组分成两部分,arr[i-1]之前有序区,arr[i]之后无序区 //当无序区的第一个数arr[i]小于有序区的最后一个数arr[i-1]时,需要将arr[i]插入到有序区中 if(arr[i] < arr[i-1]) { //把当前需要插入的数字保存起来,以便有序区中比它大的数字向后挪 int tmp = arr[i]; int j; //从有序中确定tmp的位置,比tmp大的数字往后面挪,然后将其插入 for(j = i-1;j>=0 && tmp < arr[j];j--) { arr[j+1] = arr[j];//将该数字往后挪,将该数字的值赋给后一个数字 }//当j=-1(将tmp插入到有序数组的第0个位置)、tmp>arr[j](将tmp插入到arr[j]的后面)结束循环 arr[j+1] = tmp; } } } }
希尔排序
-
时间复杂度:
- 最好时间复杂度:
- 最坏时间复杂度:
- 平均时间复杂度:
-
稳定性:
-
代码:
package 插入排序; import java.util.Arrays; public class ShellSort { public static void main(String[] args) { int[] arr = new int[] {2,5,7,3,8,1,9,0}; shellSort(arr); System.out.println(Arrays.toString(arr)); } /** * 希尔排序:将数组分成 d=n/2 个组,在每个组中采用直接插入排序 * * @param arr */ public static void shellSort(int[] arr) { int d = arr.length/2; while(d>0) { for (int i = d; i < arr.length; i++) {//对于arr[d]是需要插入有序列d的元素 //对于一组中:arr[i-d]之前有序,arr[i]之后无序,需要将arr[i]这个元素插入到有序列中 if (arr[i] < arr[i-d]) { int tmp = arr[i]; int j; for (j = i-d; (j >= 0) && (tmp < arr[j]);) { arr[j+d] = arr[j]; j = j-d; } // 循环结束后arr[j]<tmp arr[j+d] = tmp; } } d = d/2; } } }
折半插入排序:
-
时间复杂度:
- 最好时间复杂度:
- 最差时间复杂度:
- 平均时间复杂度:
-
稳定性:
-
代码:
4. 归并排序
归并思想:将两个有序的数组合并到一个数组
二路归并排序
思想(递归):将数组左边的元素排好序,将数组右边的元素排好序,将两个有序的数组合并到一个数组。
-
时间复杂度:
- 最好时间复杂度:
- 最坏时间复杂度
- 平均时间复杂度:
-
稳定性:
-
代码:
package 归并排序; import java.util.Arrays; public class MergeSort { public static void main(String[] args) { int[] arr = new int[] {1,5,3}; mergeSort(arr,0,arr.length-1); System.out.println(Arrays.toString(arr)); } /** * 归并算法(递归) * @param arr * @param low * @param high */ public static void mergeSort(int[] arr,int low,int high) { int mid = 0; if(low<high) { mid = (low+high)/2; mergeSort(arr,low,mid); mergeSort(arr,mid+1,high); merge(arr,low,mid,high); } } /** * 归并:将两个有序的数组合成一个新的有序数组 * @param arr * @param low * @param mid * @param high */ public static void merge(int[] arr,int low,int mid,int high) { //用于存储归并后的临时数组 int[] temp = new int[high-low+1]; //记录第一个数组中需要遍历的下标 int i = low; //记录第二个数组中需要遍历的下标 int j = mid+1; //用于记录临时数组的下标 int index = 0; //遍历临时数组取出小的数字,放入临时数组中 while(i <= mid && j<= high) { if(arr[i] < arr[j]) { temp[index++] = arr[i]; i++; } else { temp[index++] = arr[j]; j++; } } //将较长数组的其它元素加入到temp的后面 while(i <= mid) { temp[index++] = arr[i]; i++; } while(j <= high) { temp[index++] = arr[j]; j++; } //把临时数组中的数据放入原数组中 for(int k = 0;k<temp.length;k++) { arr[k+low] = temp[k]; } } }
5. 基数排序
思想:首先找出元素中值最大的元素,获取最大元素的位数来作为循环比较的次数。第一次循环:将元素安个位排序;第二次循环:按十位排序;第三次循环:按百位排序…
-
时间复杂度:
- 最好时间复杂度:
- 最坏时间复杂度:
- 平均时间复杂度:
-
稳定性:
-
代码:
-
方法一:
package 基数排序; import java.util.Arrays; //用一个二维数组来存比较后的元素,在用一个数组存放二维数组中每个数组的元素个数 public class RedixSort { public static void main(String[] args) { int[] arr = new int[] {23,6,189,45,9,287,34,56,1,798,652,5}; radixSort(arr); System.out.println(Arrays.toString(arr)); } public static void radixSort(int[] arr) { //1.先找出数组中的最大值 int max = Integer.MIN_VALUE; for(int i = 0;i < arr.length;i++) { if(arr[i] > max) { max = arr[i]; } } //2.计算最大值的位数 int maxLength = String.valueOf(max).length(); //3.根据最大值的位数决定要循环的次数:n为除数 //用于临时存储按个位、十位....比较好的元素 int[][] temp = new int[10][arr.length]; //用于记录temp数组中每个一位数组中的数组中的元素个数 int[] counts = new int[10]; for(int i = 0,n=1;i < maxLength;i++,n*=10) {//第一次循环比较个位,第二次循环比较十位,第三次循环比较百位 //将数组中的元素取余后存入桶中 for(int j = 0 ;j < arr.length;j++) { //计算余数 int ys = arr[j]/n%10; temp[ys][counts[ys]] = arr[j]; //记录数量 counts[ys]++; } //将桶中的元素一次取出 int index = 0; for(int k = 0;k<counts.length;k++) { if(counts[k] != 0) {//判断该桶中的是否有元素 //遍历取出元素 for(int m = 0;m<counts[k];m++) { arr[index++] = temp[k][m]; } //将counts中的k位置的值为0,以便下一轮存放 counts[k] = 0; } } } } }
-
方法二:
package 基数排序; //根据桶中"先进先出"的性质:直接用一个队列数组来存放比较后的值 public class RedixSortQueue { public static void radixSort(int[] arr) { // 1.先找出数组中的最大值 int max = Integer.MIN_VALUE; for (int i = 0; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } // 2.计算最大值的位数 int maxLength = String.valueOf(max).length(); // 3.根据最大值的位数决定要循环的次数:n为除数 // 用于临时存储按个位、十位....比较好的元素 MyLinkQueue[] temp = new MyLinkQueue[10]; for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {// 第一次循环比较个位,第二次循环比较十位,第三次循环比较百位 // 将数组中的元素取余后存入桶中 for (int j = 0; j < arr.length; j++) { // 计算余数 int ys = arr[j] / n % 10; temp[ys].enQueue(arr[j]); } // 将桶中的元素一次取出 int index = 0; for (int k = 0; k < temp.length; k++) { arr[index++] = temp[k].deQueue(); } } } } package 基数排序; //队列的链式存储结构 public class MyLinkQueue { class DataNode { int data; DataNode next; } private DataNode front;// 队首节点 private DataNode rear;// 队尾节点,一直指向最后一个元素 public MyLinkQueue() { front = new DataNode(); front.next = null; rear = new DataNode(); rear = null; } /** * 判断节点是否为空 * @return */ public boolean isEmpty() { return rear == null; } /** * 进队:采用尾插入法,后进队的元素在后面 * @param element * @return */ public boolean enQueue(int element) { DataNode data = new DataNode(); data.data = element; if (isEmpty()) {// 如果队列为空,则首节点和尾节点都指向它 front.next = data; rear = data; return true; } else {//否则,只将尾节点指向它 // 将新增节点插入到链表的最后面 rear.next = data; // 然后将rear尾节点指向最后一个元素 rear = data; return true; } } /** * 出队:出首节点的后节点,因为采用的是尾插入法 * @return * @throws RuntimeException */ public int deQueue() throws RuntimeException{ if(isEmpty()) { throw new RuntimeException("队列为空"); } if(front.next == rear) {//如果队列中只要一个节点时 int result = front.next.data; front.next = null; rear = null; return result; } else { int result = front.next.data; front.next = front.next.next; return result; } } }
-