第7章:排序算法
1、排序
内部排序:
将需要处理的所有数据都加载到内存中进行排序。
外部排序:
数据量过大,无法全部加载到内存中,需要借助外部存储(文件等)进行排序
时间频度:
一个算法中语句执行次数
时间复杂度:
算法中的基本操作语句的重复执行次数是问题规模n的某个函数。
空间复杂度:
临时占用存储空间大小的度量。
常见的算法时间复杂度由小到大依次为:
O
(
1
)
<
O
(
l
o
g
2
n
)
<
O
(
n
)
<
O
(
n
l
o
g
2
n
)
<
O
(
n
2
)
<
O
(
n
3
)
<
O
(
n
k
)
<
O
(
2
n
)
Ο(1)<Ο(log_2n)<Ο(n)<Ο(nlog_2n)<Ο(n^2)<Ο(n^3)< Ο(n^k) <Ο(2^n)
O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(nk)<O(2n)
- 常数阶O(1)
int i=1;
int j=2;
++i;
j++;
int m=i+j;
-
对数阶
O ( l o g 2 n ) O(log_2n) O(log2n)int i = 1; while (i < n) { i = i * 2; }
-
线性阶O(n)
for (i = 1; i <= n; ++i) {
j = i;
j++;
}
-
线性对称阶
O ( n l o g 2 N ) O(nlog_2N) O(nlog2N)for (m = 1; m < n; m++) { i = 1; while (i < n) { i = i * 2; } }
-
平方阶
O ( n 2 ) O(n^2) O(n2)
for (x = 0; i <= n; x++) {
for (i = 1; i <= n; i++) {
j = i;
j++;
}
}
2、冒泡排序
依次比较相邻元素的值,若发现逆序则交换。
O
(
n
2
)
O(n^2)
O(n2)
- 一共进行n-1次大循环
- 每一趟排序次数都在减少
- 某趟排序中没有发生一次交换,可以提前结束冒泡排序
//冒泡排序
public static void bubbleSort(int[] arr){
boolean flag=false;
int temp=0;
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]){
flag=true;
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
if (!flag){//这一趟没有发生交换
break;
}else{
flag=false;
}
}
}
3、选择排序
第一次从arr[0]-arr[n-1]中选取最小值与arr[0]交换,依次循环,n-1次
O
(
n
2
)
O(n^2)
O(n2)
public static void selectSort(int[] arr){
for (int i = 0; i <arr.length-1 ; i++) {
int minIndex=i; //最小值下标
int min=arr[i]; //假设最小值
for (int j = i+1; j < arr.length; j++) {
if (min>arr[j]){ //假设的不是最小值
min=arr[j]; //重置最小值和下标
minIndex=j;
}
}
if (minIndex!=i){
arr[minIndex]=arr[i]; //交换
arr[i]=min;
}
}
}
4、插入排序
把第一个元素看成是有序表,依次从无序表中取出与有序表进行比较插入。
public static void insertSort(int[] arr){
int val=0;//插入的值
int index=0;
for (int i = 1; i < arr.length; i++) {
val=arr[i];
index=i-1; //有序列表最后一个数
while (index>=0&&val<arr[index]){ //从有序列表最后一个数开始找插入位置
arr[index+1]=arr[index]; //后移
index--;
}
if (index+1!=i){
arr[index+1]=val;
}
}
}
5、希尔排序:高效的插入排序
把元素按增量进行(length/2)分组,对每组使用直接插入排序算法;
随着增量(length/2)逐渐减少,每组包含的关键词越来越多,当增量减至1时,所有元素分成一组。
public static void shellSort(int[] arr){
int temp=0;
int count=0;
for (int gap = arr.length/2; gap >0 ; gap/=2) {
for (int i = gap; i <arr.length ; i++) {
for (int j=i-gap;j>=0;j-=gap){
if (arr[j]>arr[j+gap]){
temp=arr[j];
arr[j]=arr[j+gap];
arr[j+gap]=temp;
}
}
}
}
}
6、快速排序-递归
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,递归进行。
public static void quicksort(int[] arr, int start, int end) {
int i = start;
int j = end;
int pivot = arr[(start + end) / 2];//取中间的数为基准
int temp = 0;
while (i < j) {
while (arr[i] < pivot) {
i++;
}
while (arr[j] > pivot) {
j--;
}
if (i >= j) {
break;
}
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
if (arr[i] == pivot) {
j--;
}
if (arr[j] == pivot) {
i++;
}
if (i == j) {
i++;
j--;
}
if (start < j) {
quicksort(arr, start, j);
}
if (end > i) {
quicksort(arr, i, end);
}
}
}
7、归并排序
分治策略
将两个有序[4,5,7,8]和[1,2,3,6]合并为一个有序
public class MergeSort {
public static void main(String[] args) {
int[] arr={8,9,1,7,2,3,5,4,6,0};
int[] temp=new int[arr.length];
mergeSort(arr,0,arr.length-1,temp);
System.out.println(Arrays.toString(arr));
}
//分解
public static void mergeSort(int[] arr,int left,int right,int[] temp){
if (left<right){
int mid=(left+right)/2;
mergeSort(arr,left,mid,temp);
mergeSort(arr,mid+1,right,temp);
merge(arr,left,mid,right,temp);//合并
}
}
//合并
public static void merge(int[] arr,int left,int mid,int right,int[] temp){
int i=left; //左边初始索引
int j=mid+1; //右边初始索引
int t=0; //temp当前索引
while (i<=mid&&j<=right){
if (arr[i]<=arr[j]){
temp[t]=arr[i];
t++;
i++;
}else {
temp[t]=arr[j];
t++;
j++;
}
while (i<=mid){ //左边剩余
temp[t]=arr[i];
t++;
i++;
}
while (j<=right){ //右边剩余
temp[t]=arr[j];
t++;
j++;
}
//把temp数组中的元素拷贝到arr
t=0;
int tempLeft=left;
while(tempLeft<=right){
arr[tempLeft]=temp[t];
t++;
tempLeft++;
}
}
}
}
8、基数排序(桶排序)
桶排序:空间换时间
基本思想:将所有待比较数值统一为同样数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成。
- 对桶排序的扩展
- 以空间换时间
- 稳定
- 如有负数,不使用基数排序
public class RadixSort {
public static void main(String[] args){
int[] arr={53,3,542,748,14,214};
radixSort(arr);
System.out.println(Arrays.toString(arr));
}
public static void radixSort(int[] arr){
int max=arr[0]; //获取最大值
for (int i = 0; i < arr.length; i++) {
if(arr[i]>max){
max=arr[i];
}
}
int maxLength=(max+"").length(); //最大值的位数
int[][] bucket=new int[10][arr.length]; //10个桶,最多全部数据在一个桶
int[] counts=new int[10]; //counts[0]表示第一个桶有多少个数
for (int i = 0,n=1; i <maxLength; i++,n*=10) { //个位十位百位
for (int j = 0; j < arr.length; j++) {
int digEl=arr[j]/n%10;
bucket[digEl][counts[digEl]]=arr[j];
counts[digEl]++;
}
int index=0;
for(int k=0;k<counts.length;k++){
if (counts[k]!=0){ //k桶数据
for (int j = 0; j < counts[k]; j++) {
arr[index++]=bucket[k][j]; //拷贝到数组中
}
}
counts[k]=0;
}
}
}
}