赞
踩
排序也称排序算法
(Sort Algorithm),排序是将—组数据,依指定的顺序进行排列的过程。
1)内部排序:
指将需要处理的所有数据都加载到内部存储器中进行排序。
2)外部排序法:
数据量过大,无法全部加载到内存中,需要借助外部存储进行排序。
3)常见的排序算法分类(见下图)
1)事后统计的方法
这种方法可行,但是有两个问题:
一.是要想对设计的算法的运行性能进行评测,需要实际运行该程序;
二.是所得时间的统计量依赖于计算机的硬件、软件等环境因素,这种方式,要在同一台计算机的相同状态下运行,才能比较那个算法速度更快。
2)事前估算的方法
通过分析某个算法的时间复杂度来判断哪个算法更优.
基本介绍
时间频度:一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。[举例说明]
1)一般情况下,算法中的基本操作语句的重复执行次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/ f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O( f(n)),称O( f(n))为算法的渐进时间复杂度,简称时间复杂度。
2)T(n)不同,但时间复杂度可能相同。如: T(n)=n2+7h+6与T(n)=3n2+2n+2它们的T(n)不同,但时间复杂度相同,都为O(n2)。
3)计算时间复杂度的方法:
常见的时间复杂度
1)常数阶o(1)
2)对数阶o(log2n)
3)线性阶o(n)
4)线性对数阶o(nlog2n)
5)平方阶o(n2)
6)立方阶o(n3)
7)k次方阶O(nk)
8)指数阶o(2n)
说明:
从图中可见,我们应该尽可能避免使用指数阶的算法
无论代码执行了多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就都是o(1)
上述代码在执行的时候,它消耗的时候并不随着某个变量的增长而增长,那么无论这类代码有多长,即使有几万几十万行,都可以用o(1)来表示它的时间复杂度。
说明:
在while循环里面,每次都将i乘以2,乘完之后,i距离n就越来越近了。假设循环x次之后,i就大于2了,此时这个循环就退出了,也就是说2的x次方等于n,那么x=log2n也就是说当循环log2n次以后,这个代码就结束了。因此这个代码的时间复杂度为:O(log2n).O(log2n)的这个2时间上是根据代码变化的,i=i*3,则是O(log3n).
说明:这段代码,for循环里面的代码会执行n遍,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用o(n)来表示它的时间复杂度
说明:线性对数阶o(nlogN)其实非常容易理解,将时间复杂度为o(logn)的代码循环N遍的话,那么它的时间复杂度就是n*O(logN),也就是了O(nlogN)
说明:
平方阶o(n2)就更容易理解了,如果把o(n)的代码再嵌套循环一遍,它的时间复杂度就是o(n2),这段代码其实就是嵌套了2层n循环,它的时间复杂度就是o(mn)
即o(n2)如果将其中一层循环的n改成m,那它的时间复杂度就变成了O(mn)
说明:参考上面的o(n2)去理解就好了,o(n)相当于三层n循环,其它的类似
1)平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,该算法的运行时间。
2)最坏情况下的时间复杂度称最坏时间复杂度。一般讨论的时间复杂度均是最坏情况下的时间复杂度。这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的界限,这就保证了算法的运行时间不会比最坏情况更长。
3)平均时间复杂度和最坏时间复杂度是否一致,和算法有关(如图:)。
基本介绍
1)类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)定义为该算法所耗费的存储空间,它也是问题规模n的函数。
2)空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。有的算法需要占用的临时工作单元数与解决问题的规模n有关,它随着n的增大而增大,当n较大时,将占用较多的存储单元,例如快速排序和归并排序算法就属于这种情况
3)在做算法分析时,主要讨论的是时间复杂度。从用户使用体验上看,更看重的程序执行的速度。一些缓存产品(redis, memcache)和算法(基数排序)本质就是用空间换时间.
//创建数组的第一种方法
int[] arr=new int[6]; //通过创建对象的方法来声明一个数组对象
int intValue=arr[5]; //获取arr数组中第五号元素的值
//System.out.println(intValue);
//创建数组的第二种方法
int[] x={1,2,3,4}; //通过{}来创建
//System.out.println(x[1]);
//创建数组的第三种方法。
int[] y= new int[]{1,2,3,4,5};
int m=0;
boolean length = isLength(m,y); //防止数组的长度比0还小
if(length){
System.out.println(y[m]);
}else{
System.err.println("数组标越界");
}
上述例子所用到的方法
//判断数组下标是否越界
public static boolean isLength(int m,int arr[]){
boolean flag=false;
int length = arr.length;
if(m<length)
flag=true;
return flag;
}
//第四种方法 在leetcode刷题中看到的方法
int[] x=new int[]{1, 2}; //得到数组对象[1,2]
小结上面的图解过程:
(1)一共进行数组的大小-1 次大的循环
⑵每一趟排序的次数在逐渐的减少
(3)如果我们发现在某趟排序中,没有发生一次交换,可以提前结束冒泡排序。这个就是优化
注意:
package com.iswhl.sort;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
//冒泡排序
int temp = 0;
//创建数组array[]的同时给该数组赋值,其值使用{}来进行赋予
int array[] = {3,9,-1,10,-2};
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j <array.length - 1 -i; j++) {
if (array[j] > array[j+1]){
temp = array[j+1];
array[j+1] = array[j];
array[j] = temp;
}
}
System.out.printf("第%d次冒泡排序:\n",i+1);
System.out.println(Arrays.toString(array));
}
}
}
package com.iswhl.sort;
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int array[] = {3, 9, -1, 10, 20};
bubbleSort(array);
}
public static void bubbleSort(int[] array) {
//冒泡排序
int temp = 0;
Boolean flag = false;//设置标准位
//创建数组array[]的同时给该数组赋值,其值使用{}来进行赋予
for (int i = 0; i < array.length - 1; i++) {
for (int j = 0; j < array.length - 1 - i; j++) {
if (array[j] > array[j + 1]) {
flag = true;
temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}
}
System.out.printf("第%d次冒泡排序:\n", i + 1);
System.out.println(Arrays.toString(array));
if (!flag) {
//若满足该条件则说明,该数组没有进行一次排序,结束程序
break;
} else {
flag = false;//否则将flag设置会false
}
}
}
}
选择式排序也属于内部排序法,是从欲排序的数据中,按指定的规则选出某一元素,再依规定交换位置后达到排序的目的。
选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从arr[o]~arr[n-1]中选取最小值,与arr[o]交换,第二次从
arr[1]arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]arr[n-1]中选取最小值,与arr[2]交换,…,第i次从arr[i-1]arr[n-1]中选取最小值,与arr[i-1]交换,…,第n-1次从arr[n-2]arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。
说明:
1.选择排序一共有数组大小-1轮排序
2.每1轮排序,又是一个循环,循环的规则(代码)
2.1先假定当前这个数是最小数
2.2然后和后面的每个数进行比较,如果发现有比当前数更小的数,就重新确定最小数,得到下标
2.3当遍历到数组的最后时,就得到本轮最小数和下标
2.4交换
package com.iswhl.sort;
import java.util.Arrays;
public class SelectSort {
public static void main(String[] args) {
int array[] = {23,13,100,1};
selectSort(array);
}
public static void selectSort(int[] array){
int k;//用来记录当前的下表值
int temp;//用来做交换时的变量
for (int i = 0; i < array.length - 1; i++) {
k = i;//假定当前的k下标的值就是,当前要排序的最小值
for (int j = i+1; j < array.length; j++) {
if (array[k] > array[j]){//通过与当前设置的最小值进行比较,找到比当前所认定的最小值,还小的值
k = j;//记录找到的最小值的下标
}
}
if (k != i){//说明我们假定的最小值,并不是最小值,所以我们需要给假设的最小值重新赋值
//当前的k = j
temp = array[i];
array[i] = array[k];
array[k] = temp;
}
System.out.printf("第%d次交换:",i+1);
System.out.println(Arrays.toString(array));
}
}
}
插入式排序属于内部排序法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的。
插入排序( Insertion Sorting)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表,开始时有序表中只包含一个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,把它的排序码依次与有序表元素的排序码进行比较,将它插入到有序表中的适当位置,使之成为新的有序表。
package com.iswhl.sort;
import java.lang.reflect.Array;
import java.util.Arrays;
public class InsterSort {
public static void main(String[] args) {
int array[] = {1,3,5,2,6};
inserSort(array);
}
//插入排序
public static void inserSort(int[] array){
for (int i = 1; i < array.length; i++) {
int indexValue = array[i];
int index = i - 1;
//index >= 0 防止array数组越界
//indexValue > array[index] 开始寻找要插入的值的位置
while (index >= 0 && indexValue < array[index]){
//如果满足该条件,说明当前的位置比它前一个位置要小
//就需要把其前一个位置,后移
array[index+1] = array[index];
//将其值往前移一个位置,直到找到跳出while循环
index--;
}
//通过上面的循环,可以找到符合插入位置的前一个位置
array[++index] = indexValue;
System.out.printf("第%d次循环",i);
System.out.println(Arrays.toString(array));
}
}
}
希尔排序是希尔(Donald Shell)于1959年提出的一种排序算法。希尔排序也是一种插入排序,它是简单插入排序经过改进之后的一个更高效的版本,也称为缩小增量排序。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐械少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止
有一群小牛,考试成绩分别是{8,9,1,7,2,3,5,4,6,0}请从小到大排序.请分别使用
1)希尔排序时,对有序序列在插入时采用交换法,并测试排序速度.
2)希尔排序时,对有序序列在插入时采用移动法,并测试排序速度
package com.iswhl.sort;
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
int array[] = {8,9,1,7,2,3,5,4,6,0};
shellSort(array);
}
public static void shellSort(int[] array){
//希尔排序,将一个数组/2后进行分组
//通过gap将一个数组不停的进行/2
int temp = 0;//用于交换
int count = 0;
for (int gap = array.length / 2; gap > 0; gap /= 2) {
//进行每个分组的遍历
for (int i = gap; i < array.length; i++) {
//i为每个分组中最后的值
for (int j = i-gap; j >= 0 ; j -= gap) {
//遍历每个分组中的值
//如果该组的后面的值,小于其该组前一个值则进行交换
if (array[j] > array[j+gap]){
temp = array[j];
array[j] = array[j+gap];
array[j+gap] = temp;
}
}
}
System.out.printf("第%d次排序:\n",++count);
System.out.println(Arrays.toString(array));
}
}
}
package com.iswhl.sort;
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
int array[] = {8,9,1,7,2,3,5,4,6,0};
shellSort(array);
System.out.println("移位排序:");
shellSort2(array);
}
public static void shellSort(int[] array){
//希尔排序,将一个数组/2后进行分组
//通过gap将一个数组不停的进行/2
int temp = 0;//用于交换
int count = 0;
for (int gap = array.length / 2; gap > 0; gap /= 2) {
//进行每个分组的遍历
for (int i = gap; i < array.length; i++) {
//i为每个分组中最后的值
for (int j = i-gap; j >= 0 ; j -= gap) {
//遍历每个分组中的值
//如果该组的后面的值,小于其该组前一个值则进行交换
if (array[j] > array[j+gap]){
temp = array[j];
array[j] = array[j+gap];
array[j+gap] = temp;
}
}
}
System.out.printf("第%d次排序:\n",++count);
System.out.println(Arrays.toString(array));
}
}
public static void shellSort2(int[] array) {
//希尔排序,将一个数组/2后进行分组
//通过gap将一个数组不停的进行/2
int count = 0;
int indexValue;
int index;
for (int gap = array.length / 2; gap > 0; gap /= 2) {
//进行每个分组的遍历
for (int i = gap; i < array.length; i++) {
indexValue = array[i];
index = i - gap;
while (index >= 0 && indexValue < array[index]){
array[index+gap] = array[index];
index -= gap;
}
array[index+gap] = indexValue;
}
System.out.printf("第%d次排序:\n",++count);
System.out.println(Arrays.toString(array));
}
}
}
快速排序(Quicksort)是对冒泡排序的一种改进。
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
package com.iswhl.sort;
import java.util.Arrays;
public class QuickSort {
public static void main(String[] args) {
int[] arr = {-9,78,0,23,-567,70};
quickSort(arr, 0,arr.length-1 );
System.out.println("arr: "+ Arrays.toString(arr));
}
public static void quickSort(int[] array,int left,int right){
int l = left;//左下标
int r = right;//右下标
int temp = 0;//临时变量
//pivot 中轴
int pivot = array[(left + right)/2];
//只要右边的索引大于左边的索引就继续循环 目的是让 比pivot小的值放在 比 pivot 值大的右边
while (l<r){
//在pivot的左边一直找 找到大于等于pivot的值 才退出
while (array[l]<pivot){
l += 1;
}
//在pivot的右边一直找 找到大于pivot的值 才退出
while (array[r]>pivot){
r -= 1;
}
//如果条件成立,说明pivot左边的值 已经全部都小于等于 pviot的值
//右边的值全都 大于等于pviot的值
if (l>= r){
break;
}
//交换
temp = array[l];
array[l] = array[r];
array[r] = temp;
//判断交换完了以后
if (array[l] == pivot){
r -= 1;
}else if (array[r] == pivot){
l += 1;
}
}
// 如果 l == r 必须 l++ 和 r--, 否则会出现栈溢出
if (l == r ){
l++;
r--;
}
//左递归
if (left<r){
quickSort(array,left,r);
}
//右递归
if (right>l){
quickSort(array,l,right);
}
}
}
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer〉策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
package com.iswhl.sort;
import java.util.Arrays;
public class MergetSort {
public static void main(String[] args) {
int arr[] = {8,4,5,7,1,2,6,3};
int temp[] = new int[arr.length];
mergetSort(arr,0,arr.length-1,temp);
System.out.println(Arrays.toString(arr));
}
/**
* 分解+合并
*
* @param arr
* @param left
* @param right
* @param temp
*/
public static void mergetSort(int[] arr,int left,int right,int[] temp){
if (left < right){
int mid = (left + right)/2;
//向左递归分解
mergetSort(arr,left,mid,temp);
//向右递归分解
mergetSort(arr, mid+1, right, temp);
//合并
merget(arr,left,mid,right,temp);
}
}
/**
*合并
*
* @param arr 需要排序的数组
* @param left 传进来的数组左边的下标
* @param mid 中间下标
* @param right 右边下标
* @param temp 用来做中间数组
*/
public static void merget(int[] arr,int left,int mid,int right,int[] temp){
System.out.println("arr="+Arrays.toString(arr)+"left="+left+"mid="+mid+"right="+right+"temp="+Arrays.toString(temp));
int i = left;//初始化i,为该数组的左边的初始索引
int j = mid + 1;//初始化j,为该数组的右边的的初始索引
int t = 0;//用来当前做temp中间数组的遍历指针
//1.
//先把左右两边(有序)的数据按照规则填充到temp数组中
//直到有一边填充结束
while (i <= mid && j <= right){
//如果左边的有序序列的当前值,小于等于右边有序序列的当前值
//即将左边的值,放入临时的数组temp中
//然后t++ i++
if (arr[i] <= arr[j]){
temp[t] = arr[i];
t++;
i++;
}else {//否则就是右边的数组小于左边的,即将右边的加入到temp,t++ j++
temp[t] = arr[j];
t++;
j++;
}
}
//2.
//把有剩余数据的右边的数据,都依次加入的temp的临时数组中去
while (i <= mid){//左边还有剩余的数值,则添加到temp中
temp[t] = arr[i];
t++;
i++;
}
while (j <= right){//右边还有剩余的数值,则添加到temp中
temp[t] = arr[j];
t++;
j++;
}
//3.
//将temp中的数组copy到arr中去
//注意:并不是每次都拷贝全部的数组
System.out.println("tempLeft="+left+" right="+right);
t = 0;
int tempLeft = left;
while (tempLeft <= right){
arr[tempLeft] = temp[t];
t++;
tempLeft++;
}
}
}
1)基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
2)基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
3)基数排序(Radix Sort)是桶排序的扩展
4)基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。
1)将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。
2)这样说明,比较难理解,下面我们看一个图文解释,理解基数排序的步骤
1)基数排序是对传统桶排序的扩展,速度很快.
2)基数排序是经典的空间换时间的方式,占用内存很大,当对海量数据排序时,容易造成OutOfMemoryError .
3)基数排序时稳定的。[注:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]F=r[,且rli在ri]之前,而在排序后的序列中,r[]仍在r[]之前,则称这种排序算法是稳定的;否则称为不稳定的]
package com.iswhl.sort;
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int array[] = {53,3,543,748,14,214};
radixSort(array);
System.out.println(Arrays.toString(array));
}
public static void radixSort(int array[]){
//bucket为桶,用来存放数据
int[][] bucket = new int[10][array.length];
//bucketElementCounts为每个桶中的数据个数
int[] bucketElementCounts = new int[10];
int index;
//找到该数组的最大值
int max = array[0];//假定当前的第一个值就是最大值
for (int i = 0; i < array.length; i++) {
if (array[i] > max){
max = array[i];
}
}
//得到当前最大值的位数
int maxLength = (max+"").length();//这里的方法很巧妙,将其转化为String类型后.length取值
for (int i = 0,n = 1; i < maxLength; i++, n *= 10) {
//将array中的数据 按照要求放到每个对应桶中
for (int j = 0; j < array.length; j++) {
int digitOfElement = array[j] /n % 10;
//放入对应的桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = array[j];
bucketElementCounts[digitOfElement]++;
}
//将桶中的数据再放回 原来的数组中
index = 0;
for (int j = 0; j < bucket.length; j++) {
if (bucketElementCounts[j]!=0){
for (int k = 0; k < bucketElementCounts[j]; k++) {
array[index++] = bucket[j][k];
}
}
bucketElementCounts[j] = 0;
}
}
}
}
Length; i++, n *= 10) {
//将array中的数据 按照要求放到每个对应桶中
for (int j = 0; j < array.length; j++) {
int digitOfElement = array[j] /n % 10;
//放入对应的桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = array[j];
bucketElementCounts[digitOfElement]++;
}
//将桶中的数据再放回 原来的数组中
index = 0;
for (int j = 0; j < bucket.length; j++) {
if (bucketElementCounts[j]!=0){
for (int k = 0; k < bucketElementCounts[j]; k++) {
array[index++] = bucket[j][k];
}
}
bucketElementCounts[j] = 0;
}
}
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。