数据结构 java开发中常用的排序算法有哪些

Python07

数据结构 java开发中常用的排序算法有哪些,第1张

排序算法有很多,所以在特定情景中使用哪一种算法很重要。为了选择合适的算法,可以按照建议的顺序考虑以下标准:

(1)执行时间

(2)存储空间

(3)编程工作

对于数据量较小的情形,(1)(2)差别不大,主要考虑(3);而对于数据量大的,(1)为首要。

主要排序法有:

一、冒泡(Bubble)排序——相邻交换

二、选择排序——每次最小/大排在相应的位置

三、插入排序——将下一个插入已排好的序列中

四、壳(Shell)排序——缩小增量

五、归并排序

六、快速排序

七、堆排序

八、拓扑排序

一、冒泡(Bubble)排序

----------------------------------Code 从小到大排序n个数------------------------------------

void BubbleSortArray()

{

for(int i=1i<ni++)

{

for(int j=0i<n-ij++)

{

if(a[j]>a[j+1])//比较交换相邻元素

{

int temp

temp=a[j]a[j]=a[j+1]a[j+1]=temp

}

}

}

}

-------------------------------------------------Code------------------------------------------------

效率 O(n²),适用于排序小列表。

二、选择排序

----------------------------------Code 从小到大排序n个数--------------------------------

void SelectSortArray()

{

int min_index

for(int i=0i<n-1i++)

{

min_index=i

for(int j=i+1j<nj++)//每次扫描选择最小项

if(arr[j]<arr[min_index]) min_index=j

if(min_index!=i)//找到最小项交换,即将这一项移到列表中的正确位置

{

int temp

temp=arr[i]arr[i]=arr[min_index]arr[min_index]=temp

}

}

}

-------------------------------------------------Code-----------------------------------------

效率O(n²),适用于排序小的列表。

三、插入排序

--------------------------------------------Code 从小到大排序n个数-------------------------------------

void InsertSortArray()

{

for(int i=1i<ni++)//循环从第二个数组元素开始,因为arr[0]作为最初已排序部分

{

int temp=arr[i]//temp标记为未排序第一个元素

int j=i-1

while (j>=0 &&arr[j]>temp)/*将temp与已排序元素从小到大比较,寻找temp应插入的位置*/

{

arr[j+1]=arr[j]

j--

}

arr[j+1]=temp

}

}

------------------------------Code--------------------------------------------------------------

最佳效率O(n);最糟效率O(n²)与冒泡、选择相同,适用于排序小列表

若列表基本有序,则插入排序比冒泡、选择更有效率。

四、壳(Shell)排序——缩小增量排序

-------------------------------------Code 从小到大排序n个数-------------------------------------

void ShellSortArray()

{

for(int incr=3incr<0incr--)//增量递减,以增量3,2,1为例

{

for(int L=0L<(n-1)/incrL++)//重复分成的每个子列表

{

for(int i=L+incri<ni+=incr)//对每个子列表应用插入排序

{

int temp=arr[i]

int j=i-incr

while(j>=0&&arr[j]>temp)

{

arr[j+incr]=arr[j]

j-=incr

}

arr[j+incr]=temp

}

}

}

}

--------------------------------------Code-------------------------------------------

适用于排序小列表。

效率估计O(nlog2^n)~O(n^1.5),取决于增量值的最初大小。建议使用质数作为增量值,因为如果增量值是2的幂,则在下一个通道中会再次比较相同的元素。

壳(Shell)排序改进了插入排序,减少了比较的次数。是不稳定的排序,因为排序过程中元素可能会前后跳跃。

五、归并排序

----------------------------------------------Code 从小到大排序---------------------------------------

void MergeSort(int low,int high)

{

if(low>=high) return//每个子列表中剩下一个元素时停止

else int mid=(low+high)/2/*将列表划分成相等的两个子列表,若有奇数个元素,则在左边子列表大于右侧子列表*/

MergeSort(low,mid)//子列表进一步划分

MergeSort(mid+1,high)

int [] B=new int [high-low+1]//新建一个数组,用于存放归并的元素

for(int i=low,j=mid+1,k=lowi<=mid &&j<=highk++)/*两个子列表进行排序归并,直到两个子列表中的一个结束*/

{

if (arr[i]<=arr[j])

{

B[k]=arr[i]

I++

}

else

{ B[k]=arr[j]j++}

}

for( j<=highj++,k++)//如果第二个子列表中仍然有元素,则追加到新列表

B[k]=arr[j]

for( i<=midi++,k++)//如果在第一个子列表中仍然有元素,则追加到新列表中

B[k]=arr[i]

for(int z=0z<high-low+1z++)//将排序的数组B的 所有元素复制到原始数组arr中

arr[z]=B[z]

}

-----------------------------------------------------Code---------------------------------------------------

效率O(nlogn),归并的最佳、平均和最糟用例效率之间没有差异。

适用于排序大列表,基于分治法。

六、快速排序

------------------------------------Code--------------------------------------------

/*快速排序的算法思想:选定一个枢纽元素,对待排序序列进行分割,分割之后的序列一个部分小于枢纽元素,一个部分大于枢纽元素,再对这两个分割好的子序列进行上述的过程。*/ void swap(int a,int b){int tt =a a =b b =t }

int Partition(int [] arr,int low,int high)

{

int pivot=arr[low]//采用子序列的第一个元素作为枢纽元素

while (low <high)

{

//从后往前栽后半部分中寻找第一个小于枢纽元素的元素

while (low <high &&arr[high] >= pivot)

{

--high

}

//将这个比枢纽元素小的元素交换到前半部分

swap(arr[low], arr[high])

//从前往后在前半部分中寻找第一个大于枢纽元素的元素

while (low <high &&arr [low ]<=pivot )

{

++low

}

swap (arr [low ],arr [high ])//将这个枢纽元素大的元素交换到后半部分

}

return low //返回枢纽元素所在的位置

}

void QuickSort(int [] a,int low,int high)

{

if (low <high )

{

int n=Partition (a ,low ,high )

QuickSort (a ,low ,n )

QuickSort (a ,n +1,high )

}

}

----------------------------------------Code-------------------------------------

平均效率O(nlogn),适用于排序大列表。

此算法的总时间取决于枢纽值的位置;选择第一个元素作为枢纽,可能导致O(n²)的最糟用例效率。若数基本有序,效率反而最差。选项中间值作为枢纽,效率是O(nlogn)。

基于分治法。

七、堆排序

最大堆:后者任一非终端节点的关键字均大于或等于它的左、右孩子的关键字,此时位于堆顶的节点的关键字是整个序列中最大的。

思想:

(1)令i=l,并令temp= kl

(2)计算i的左孩子j=2i+1

(3)若j<=n-1,则转(4),否则转(6)

(4)比较kj和kj+1,若kj+1>kj,则令j=j+1,否则j不变;

(5)比较temp和kj,若kj>temp,则令ki等于kj,并令i=j,j=2i+1,并转(3),否则转(6)

(6)令ki等于temp,结束。

-----------------------------------------Code---------------------------

void HeapSort(SeqIAst R)

{ //对R[1..n]进行堆排序,不妨用R[0]做暂存单元int I BuildHeap(R); //将R[1-n]建成初始堆for(i=ni>1;i--) //对当前无序区R[1..i]进行堆排序,共做n-1趟。{ R[0]=R[1]R[1]=R[i]R[i]=R[0]//将堆顶和堆中最后一个记录交换 Heapify(R,1,i-1) //将R[1..i-1]重新调整为堆,仅有R[1]可能违反堆性质 }} ---------------------------------------Code--------------------------------------

堆排序的时间,主要由建立初始堆和反复重建堆这两部分的时间开销构成,它们均是通过调用Heapify实现的。

堆排序的最坏时间复杂度为O(nlgn)。堆排序的平均性能较接近于最坏性能。 由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。 堆排序是就地排序,辅助空间为O(1), 它是不稳定的排序方法。

堆排序与直接插入排序的区别:

直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。

堆排序可通过树形结构保存部分比较结果,可减少比较次数。

八、拓扑排序

例 :学生选修课排课先后顺序

拓扑排序:把有向图中各顶点按照它们相互之间的优先关系排列成一个线性序列的过程。

方法:

在有向图中选一个没有前驱的顶点且输出

从图中删除该顶点和所有以它为尾的弧

重复上述两步,直至全部顶点均已输出(拓扑排序成功),或者当图中不存在无前驱的顶点(图中有回路)为止。

---------------------------------------Code--------------------------------------

void TopologicalSort()/*输出拓扑排序函数。若G无回路,则输出G的顶点的一个拓扑序列并返回OK,否则返回ERROR*/

{

int indegree[M]

int i,k,j

char n

int count=0

Stack thestack

FindInDegree(G,indegree)//对各顶点求入度indegree[0....num]

InitStack(thestack)//初始化栈

for(i=0i<G.numi++)

Console.WriteLine("结点"+G.vertices[i].data+"的入度为"+indegree[i])

for(i=0i<G.numi++)

{

if(indegree[i]==0)

Push(thestack.vertices[i])

}

Console.Write("拓扑排序输出顺序为:")

while(thestack.Peek()!=null)

{

Pop(thestack.Peek())

j=locatevex(G,n)

if (j==-2)

{

Console.WriteLine("发生错误,程序结束。")

exit()

}

Console.Write(G.vertices[j].data)

count++

for(p=G.vertices[j].firstarcp!=NULLp=p.nextarc)

{

k=p.adjvex

if (!(--indegree[k]))

Push(G.vertices[k])

}

}

if (count<G.num)

Cosole.WriteLine("该图有环,出现错误,无法排序。")

else

Console.WriteLine("排序成功。")

}

----------------------------------------Code--------------------------------------

算法的时间复杂度O(n+e)。

JAVA中在运用数组进行排序功能时,一般有四种方法:快速排序法、冒泡法、选择排序法、插入排序法。

快速排序法主要是运用了Arrays中的一个方法Arrays.sort()实现。

冒泡法是运用遍历数组进行比较,通过不断的比较将最小值或者最大值一个一个的遍历出来。

选择排序法是将数组的第一个数据作为最大或者最小的值,然后通过比较循环,输出有序的数组。

插入排序是选择一个数组中的数据,通过不断的插入比较最后进行排序。下面我就将他们的实现方法一一详解供大家参考。

<1>利用Arrays带有的排序方法快速排序

public class Test2{   

        public static void main(String[] args){   

                int[] a={5,4,2,4,9,1}   

                Arrays.sort(a)  //进行排序   

                for(int i: a){   

                        System.out.print(i)   

               }  

        }  

}

<2>冒泡排序算法

public static int[] bubbleSort(int[] args){//冒泡排序算法   

        for(int i=0i<args.length-1i++){   

               for(int j=i+1j<args.lengthj++){   

                   if (args[i]>args[j]){   

                       int temp=args[i]   

                       args[i]=args[j]   

                       args[j]=temp   

                       }   

            }  

       }  

       return args  

}

<3>选择排序算法

public static int[] selectSort(int[] args){//选择排序算法   

         for (int i=0i<args.length-1 i++ ){   

                int min=i   

                for (int j=i+1j<args.length j++ ){   

                      if (args[min]>args[j]){   

                           min=j   

                           }   

                  }   

                      if (min!=i){  

                      int temp=args[i]  

                      args[i]=args[min]  

                      args[min]=temp          

                  }  

            }  

             return args  

    }

<4>插入排序算法

public static int[] insertSort(int[] args){//插入排序算法   

                 for(int i=1i<args.lengthi++){   

                         for(int j=ij>0j--){   

                                 if (args[j]<args[j-1]){   

                                         int temp=args[j-1]  

                                         args[j-1]=args[j]  

                                        args[j]=temp          

                                }else break   

                        }  

                 }  

                 return args  

         }

一、冒泡排序

[java] view plain copy

package sort.bubble

import java.util.Random

/**

* 依次比较相邻的两个数,将小数放在前面,大数放在后面

* 冒泡排序,具有稳定性

* 时间复杂度为O(n^2)

* 不及堆排序,快速排序O(nlogn,底数为2)

* @author liangge

*

*/

public class Main {

public static void main(String[] args) {

Random ran = new Random()

int[] sort = new int[10]

for(int i = 0 i <10 i++){

sort[i] = ran.nextInt(50)

}

System.out.print("排序前的数组为")

for(int i : sort){

System.out.print(i+" ")

}

buddleSort(sort)

System.out.println()

System.out.print("排序后的数组为")

for(int i : sort){

System.out.print(i+" ")

}

}

/**

* 冒泡排序

* @param sort

*/

private static void buddleSort(int[] sort){

for(int i=1i<sort.lengthi++){

for(int j=0j<sort.length-ij++){

if(sort[j]>sort[j+1]){

int temp = sort[j+1]

sort[j+1] = sort[j]

sort[j] = temp

}

}

}

}

}

二、选择排序

[java] view plain copy

package sort.select

import java.util.Random

/**

* 选择排序

* 每一趟从待排序的数据元素中选出最小(或最大)的一个元素,

* 顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。

* 选择排序是不稳定的排序方法。

* @author liangge

*

*/

public class Main {

public static void main(String[] args) {

Random ran = new Random()

int[] sort = new int[10]

for (int i = 0i <10i++) {

sort[i] = ran.nextInt(50)

}

System.out.print("排序前的数组为")

for (int i : sort) {

System.out.print(i + " ")

}

selectSort(sort)

System.out.println()

System.out.print("排序后的数组为")

for (int i : sort) {

System.out.print(i + " ")

}

}

/**

* 选择排序

* @param sort

*/

private static void selectSort(int[] sort){

for(int i =0i<sort.length-1i++){

for(int j = i+1j<sort.lengthj++){

if(sort[j]<sort[i]){

int temp = sort[j]

sort[j] = sort[i]

sort[i] = temp

}

}

}

}

}

三、快速排序

[java] view plain copy

package sort.quick

/**

* 快速排序 通过一趟排序将要排序的数据分割成独立的两部分, 其中一部分的所有数据都比另外一部分的所有数据都要小,

* 然后再按此方法对这两部分数据分别进行快速排序, 整个排序过程可以递归进行,以此达到整个数据变成有序序列。

* @author liangge

*

*/

public class Main {

public static void main(String[] args) {

int[] sort = { 54, 31, 89, 33, 66, 12, 68, 20 }

System.out.print("排序前的数组为:")

for (int data : sort) {

System.out.print(data + " ")

}

System.out.println()

quickSort(sort, 0, sort.length - 1)

System.out.print("排序后的数组为:")

for (int data : sort) {

System.out.print(data + " ")

}

}

/**

* 快速排序

* @param sort 要排序的数组

* @param start 排序的开始座标

* @param end 排序的结束座标

*/

public static void quickSort(int[] sort, int start, int end) {

// 设置关键数据key为要排序数组的第一个元素,

// 即第一趟排序后,key右边的数全部比key大,key左边的数全部比key小

int key = sort[start]

// 设置数组左边的索引,往右移动判断比key大的数

int i = start

// 设置数组右边的索引,往左移动判断比key小的数

int j = end

// 如果左边索引比右边索引小,则还有数据没有排序

while (i <j) {

while (sort[j] >key &&j >start) {

j--

}

while (sort[i] <key &&i <end) {

i++

}

if (i <j) {

int temp = sort[i]

sort[i] = sort[j]

sort[j] = temp

}

}

// 如果左边索引比右边索引要大,说明第一次排序完成,将sort[j]与key对换,

// 即保持了key左边的数比key小,key右边的数比key大

if (i >j) {

int temp = sort[j]

sort[j] = sort[start]

sort[start] = temp

}

//递归调用

if (j >start &&j <end) {

quickSort(sort, start, j - 1)

quickSort(sort, j + 1, end)

}

}

}

[java] view plain copy

/**

* 快速排序

*

* @param a

* @param low

* @param high

*voidTest

*/

public static void kuaisuSort(int[] a, int low, int high)

{

if (low >= high)

{

return

}

if ((high - low) == 1)

{

if (a[low] >a[high])

{

swap(a, low, high)

return

}

}

int key = a[low]

int left = low + 1

int right = high

while (left <right)

{

while (left <right &&left <= high)// 左边向右

{

if (a[left] >= key)

{

break

}

left++

}

while (right >= left &&right >low)

{

if (a[right] <= key)

{

break

}

right--

}

if (left <right)

{

swap(a, left, right)

}

}

swap(a, low, right)

kuaisuSort(a, low, right)

kuaisuSort(a, right + 1, high)

}

四、插入排序

[java] view plain copy

package sort.insert

/**

* 直接插入排序

* 将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据

* 算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。

*/

import java.util.Random

public class DirectMain {

public static void main(String[] args) {

Random ran = new Random()

int[] sort = new int[10]

for (int i = 0i <10i++) {

sort[i] = ran.nextInt(50)

}

System.out.print("排序前的数组为")

for (int i : sort) {

System.out.print(i + " ")

}

directInsertSort(sort)

System.out.println()

System.out.print("排序后的数组为")

for (int i : sort) {

System.out.print(i + " ")

}

}

/**

* 直接插入排序

*

* @param sort

*/

private static void directInsertSort(int[] sort) {

for (int i = 1i <sort.lengthi++) {

int index = i - 1

int temp = sort[i]

while (index >= 0 &&sort[index] >temp) {

sort[index + 1] = sort[index]

index--

}

sort[index + 1] = temp

}

}

}

顺便添加一份,差不多的

[java] view plain copy

public static void charuSort(int[] a)

{

int len = a.length

for (int i = 1i <leni++)

{

int j

int temp = a[i]

for (j = ij >0j--)//遍历i之前的数字

{

//如果之前的数字大于后面的数字,则把大的值赋到后面

if (a[j - 1] >temp)

{

a[j] = a[j - 1]

} else

{

break

}

}

a[j] = temp

}

}

把上面整合起来的一份写法:

[java] view plain copy

/**

* 插入排序:

*

*/

public class InsertSort {

public void sort(int[] data) {

for (int i = 1i <data.lengthi++) {

for (int j = i(j >0) &&(data[j] <data[j - 1])j--) {

swap(data, j, j - 1)

}

}

}

private void swap(int[] data, int i, int j) {

int temp = data[i]

data[i] = data[j]

data[j] = temp

}

}

五、顺便贴个二分搜索法

[java] view plain copy

package search.binary

public class Main {

public static void main(String[] args) {

int[] sort = {1,2,3,4,5,6,7,8,9,10}

int mask = binarySearch(sort,6)

System.out.println(mask)

}

/**

* 二分搜索法,返回座标,不存在返回-1

* @param sort

* @return

*/

private static int binarySearch(int[] sort,int data){

if(data<sort[0] || data>sort[sort.length-1]){

return -1

}

int begin = 0

int end = sort.length

int mid = (begin+end)/2

while(begin <= end){

mid = (begin+end)/2

if(data >sort[mid]){

begin = mid + 1

}else if(data <sort[mid]){

end = mid - 1

}else{

return mid

}

}

return -1

}

}