目录
1. 交换排序: 冒泡排序,快速排序.
2. 选择排序: 简单选择排序,堆排序.
3. 插入排序: 直接插入排序,二分法插入排序,希尔排序.
4. 归并排序
5. 基数排序
import java.util.ArrayList;
import java.util.List;
public class Sort {
/**
* 1. 交换排序:冒泡排序,快速排序.
*/
/**
* 1.1 冒泡排序: 在要排序的一组数中,对当前还未排好序的范围内的全部数,
* 自上而下对相邻的两个数依次进行比较和调整,较小的往上冒,较大的下沉.
*
* EX: int[] nums={8,1,4,2,23,10}; <
*
* 1,8,4,2,23,10
* 1,4,8,2,23,10
* 1,4,2,8,23,10
* 1,4,2,8,23,10
* 1,4,2,8,10,23
*
* 1,4,2,8,10,23
* 1,2,4,8,10,23
*
* 冒泡排序是一种稳定的排序方法.
* 若文件初状为正序,则一趟起泡就可完成排序,排序码的比较次数为n-1,且没有记录移动,时间复杂度是O(n);
* 若文件初态为逆序,需要n-1趟起泡,每趟进行n-i次排序码的比较,且每次比较都移动三次,比较和移动次数均达到最大值∶O(n2);
* 起泡排序平均时间复杂度为O(n2).
*/
public static void bubbleSort(int[] nums) {
for(int i=0;i<nums.length-1;i++){
for(int j=0;j<nums.length-1-i;j++){
int temp=0;
if(!(nums[j]<nums[j+1])){
temp=nums[j];
nums[j]=nums[j+1];
nums[j+1]=temp;
}
}
}
}
/**
* 1.2 快速排序: 首先任意选取一个数据(通常选第一个)作为关键数据,
* 然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,
* 即确定该数的最终位置,该数两边为两个区间,在它们中各找一个关键
* 数据继续进行前面的操作,直到所有区间都只有一个数为止,表示所有数
* 的最终位置都被确定
*
* 一趟快速排序的算法是:
* 1)设置两个变量i,j,排序开始的时候:i=0,j=N-1;
* 2)以第一个数组元素作为关键数据,赋值给key,即key=nums[0];
* 3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值nums[j],将nums[j]和nums[i](key)互换;
* 4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的nums[i],将nums[i]和nums[j](key)互换;
* ......
* 5)当i=j时,该轮排序结束,此时i,j都指向key,key所在的位置i或j即为分割点.
*
* EX: int[] nums={8(key),1,4,2,23,10}; <
*
* 8(key),1,4,2,23,10
* 2,1,4,8(key),23,10
*
* 2(key),1,4 | 8 | 23(key),10
* 1,2,4 | 8 | 10,23
*
* 1(key) | 2 | 4(key) | 8 | 10(key) | 23
*
* 快速排序是不稳定的排序。
* 快速排序的时间复杂度为O(nlogn)。
* 当n较大时使用比较好,当序列基本有序时不好。
*
*/
public static void quickSort(int[] nums, int head, int tail) {
if(head>=tail)
return ;
int i=head;
int j=tail;
int key=nums[i];
int temp=0;
while(i!=j){
while(i!=j){
if(key>nums[j]){
temp=nums[j];
nums[j]=nums[i];
nums[i]=temp;
break;
}else {
j--;
}
}
while(i!=j){
if(nums[i]>key){
temp=nums[j];
nums[j]=nums[i];
nums[i]=temp;
break;
}else {
i++;
}
}
}
int mid=i;
quickSort(nums,head,mid-1);
quickSort(nums,mid+1,tail);
}
/**
* 2. 选择排序:简单选择排序,堆排序.
*/
/**
* 2.1 简单选择排序: 在要排序的一组数中,选出最小的一个数与第一个位置的数交换;
* 然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最
* 后一个数比较为止.
*
* EX: int[] nums={8,1,4,2,23,10}; <
*
* 8,1(min),4,2,23,10
* |
* 1(min),8,4,2,23,10
*
* 1,8,4,2(min),23,10
* |
* 1,2(min),4,8,23,10
*
* 1,2,4(min),8,23,10
* |
* 1,2,4(min),8,23,10
*
* 1,2,4,8(min),23,10
* |
* 1,2,4,8(min),23,10
*
* 1,2,4,8,23,10(min)
* |
* 1,2,4,8,10(min),23
*
* 简单选择排序是不稳定的排序.
* 时间复杂度:T(n)=O(n2).
*
*/
public static void simpleSelectionSort(int[] nums) {
int min_index;
int temp=0;
for(int i=0;i<nums.length-1;i++){
min_index=i;
for(int j=i+1;j<nums.length;j++){
if(nums[min_index]>nums[j]){
min_index=j;
}
}
temp=nums[min_index];
nums[min_index]=nums[i];
nums[i]=temp;
}
}
/**
* 2.2 堆排序: 堆排序是一种树形选择排序,是对直接选择排序的有效改进;
* 堆的定义: 具有n个元素的序列 (h1,h2,...,hn),当且仅当满足
* (hi>=h2i,hi>=2i+1)(大顶堆) 或 (hi<=h2i,hi<=2i+1)(小顶堆) (i=1,2,...,n/2)时称之为堆,
* 即任何一非叶节点的大于等于或者小于等于其左右孩子节点的值;
* 在这里只讨论满足前者条件(hi>=h2i,hi>=2i+1)的堆(大顶堆):最后得到顺序;
* 由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项;
* 完全二叉树可以表示堆的结构,堆顶为根,其它为左子树、右子树.
*
* 步骤:
* (1)建立大顶堆
* (2)堆顶与最右叶子节点(最后一个节点)交换
* 循环...
*
* 可参考:http://blog.youkuaiyun.com/xiaoxiaoxuewen/article/details/7570621/
*
* 堆排序是一种不稳定的排序算法:
* 堆排序的最坏时间复杂度为O(nlogn).
* 堆序的平均性能较接近于最坏性能.
* 由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件.
* 堆排序优于简单选择排序的原因:
* 直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,
* 然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较.
* 事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,
* 但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作.
* 堆排序可通过树形结构保存部分比较结果,可减少比较次数.
*
*/
public static void buildMaxHeap(int[] nums, int last_index){
int half=(last_index-1)/2;
int temp;
int max_index;
for (int i=half;i>=0;i--){
max_index=i;
int left_index=2*i+1;
int right_index=2*i+2;
if(left_index<=last_index){
if(nums[left_index]>nums[max_index]){
max_index=left_index;
}
if(right_index<=last_index){
if(nums[right_index]>nums[max_index]){
max_index=right_index;
}
}
}
if(nums[i]<nums[max_index]) {
temp=nums[i];
nums[i]=nums[max_index];
nums[max_index]=temp;
}
}
}
public static void heapSort(int[] nums) {
int temp;
for(int i=0;i<nums.length-1;i++){
buildMaxHeap(nums,nums.length-1-i);//从lastIndex处节点(最后一个节点)的父节点开始
temp=nums[0];
nums[0]=nums[nums.length-1-i];
nums[nums.length-1-i]=temp;
}
}
/**
* 3. 插入排序:直接插入排序,二分法插入排序,希尔排序.
*/
/**
* 3.1 直接插入排序: 每步将一个待排序的记录,按其顺序码大小插入到前面已经
* 排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止
*
* EX: int[] nums={8,1,4,2,23,10}; <
*
* 8,1,4,2,23,10
* |
* 1,8,4,2,23,10
* |
* 1,4,8,2,23,10
* |
* 1,2,4,8,23,10
* |
* 1,2,4,8,23,10
* |
* 1,2,4,8,10,23
*
* 直接插入排序是稳定的排序.
* 文件初态不同时,直接插入排序所耗费的时间有很大差异.
* 若文件初态为正序,则每个待插入的记录只需要比较一次就能够找到合适的位置插入,
* 故算法的时间复杂度为O(n),这时最好的情况.若初态为反序,则第i个待插入记录
* 需要比较i+1次才能找到合适位置插入,故时间复杂度为O(n2),这时最坏的情.
* 直接插入排序的平均时间复杂度为O(n2).
*
*/
public static void insertSort(int[] nums) {
int insert;
for(int i=1;i<nums.length;i++){
if(nums[i]<nums[i-1]){
insert=nums[i];
for(int j=i-1;j>=0;j--){
nums[j+1]=nums[j];
if(j==0){
nums[0]=insert;
}
else if(insert>=nums[j-1]){
nums[j]=insert;
break;
}
}
}
}
}
/**
* 3.2 二分法插入排序: 按二分法找到合适的位置,可以减少比较的次数.
*
* EX: int[] nums={8,1,4,2,23,10}; <
*
* 8,1,4,2,23,10
* |
* 1,8,4,2,23,10
* |
* 1,4,8,2,23,10
* |
* 1,2,4,8,23,10
* |
* 1,2,4,8,23,10
* |
* 1,2,4,8,10,23
*
* 二分法插入排序是稳定的.
* 二分插入排序的比较次数与待排序记录的初始状态无关,仅依赖于记录的个数.
* 当n较大时,比直接插入排序的最大比较次数少得多,但大于直接插入排序的最小比较次数.
* 算法的移动次数与直接插入排序算法的相同,最坏的情况为n2/2,最好的情况为n.
* 平均时间复杂度为O(n2).
*
*/
public static void binarySort(int[] nums) {
int half,insert;
for(int i=1;i<nums.length;i++){
half=i/2;
insert=nums[i];
if(nums[i]==nums[half]){
for(int j=i-1;j>half;j--){
nums[j+1]=nums[j];
}
nums[half+1]=insert;
}else if(nums[i]<nums[half]){
for(int j=i-1;j>half;j--){
nums[j+1]=nums[j];
}
for(int j=half;j>=0;j--){
nums[j+1]=nums[j];
if(j==0)
nums[0]=insert;
else if(insert>=nums[j-1]){
nums[j]=insert;
break;
}
}
}else if((nums[i]>nums[half])&&(nums[i]<nums[i-1])){
for(int j=i-1;j>=half+1;j--){
nums[j+1]=nums[j];
if(insert>=nums[j-1]){
nums[j]=insert;
break;
}
}
}
}
}
/**
* 3.3 希尔排序: 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组;
* 所有距离为d1的倍数的记录放在同一个组中,先在各组内进行直接插入排序;
* 取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1);
* 即所有记录放在同一组中进行直接插入排序为止,该方法实质上是一种分组插入方法.
*
* EX: int[] nums={8,1,4,2,23,10}; <
*
* d=length/2=3 8,1,4,2,23,10
* | |
* 2,1,4,8,23,10
*
* 2,1,4,8,23,10
* | |
* 2,1,4,8,23,10
*
* 2,1,4,8,23,10
* | |
* 2,1,4,8,23,10
*
* d=d/2=1 2,1,4,8,23,10
* | | | | | |
* 1,2,4,8,10,23
*
* 希尔排序是不稳定的.
* 希尔排序的时间性能优于直接插入排序.
* 希尔排序的平均时间复杂度为O(nlogn).
*
*/
public static void shellSort(int[] nums) {
int d=nums.length;
int insert;
do{
d/=2;
for(int i=0;i<d;i++){
for(int j=i+d;j<nums.length;j=j+d){
if(nums[j]<nums[j-d]){
insert=nums[j];
for(int k=j-d;k>=0;k=k-d){
nums[k+d]=nums[k];
if(k==i){
nums[i]=insert;
}
else if(insert>=nums[k-d]){
nums[k]=insert;
break;
}
}
}
}
}
}while(d!=1);
}
/**
* 4. 归并排序: 归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,
* 即把待排序序列分为若干个子序列,每个子序列是有序的,然后再把有序子序列合并为整体有序序列.
*
* 步骤:
* (1)用除二来划分数组,对最小单位为二的数组排序
* (2)合并两个数组并排序,具体方法为:
* 在两个数组中每次找最小的放到临时空间中
*
* EX: int[] nums={8,1,4,2,23,10}; <
*
* [8,1] [4] [2,23] [10]
* [1,8] [4] [2,23] [10]
*
* [1,8,4] [2,23,10]
* [1,4,8] [2,10,23]
*
* [1,4,8,2,10,23]
* [1,2,4,8,10,23]
*
* 归并排序是稳定的排序方法.
* 归并排序的时间复杂度为O(nlogn).
* 速度仅次于快速排序,一般用于对总体无序,但是各子项相对有序的数列.
*
*/
public static void mergeSort(int[] nums,int head,int tail) {
if(head>=tail)
return ;
int mid=(head+tail)/2;
mergeSort(nums, head, mid);
mergeSort(nums, mid+1, tail);
int[] temp = new int[tail-head+1];
int count=0;
int left = head;
int right = mid+1;
while(left<=mid && right<=tail){
if(nums[left]<=nums[right]){
temp[count++]=nums[left++];
}else{
temp[count++]=nums[right++];
}
}
while(left<=mid){
temp[count++] = nums[left++];
}
while(right<=tail){
temp[count++] = nums[right++];
}
count=0;
while(head<=tail){
nums[head++] = temp[count++];
}
}
/**
* 5. 基数排序: 将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零.
* 从最低位开始,依次进行一次排序;这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列.
*
* 步骤:
* (1)找到最大数
* (2)确定最大数的位数
* (3)把数组中的所有数都修改成为最大位数形式的,高位用0补充
* (4)建立10个数组分别表示单个位为0-9
* (5)把数组中的数分配到表示各位的数组中
* (6)按当前位置排序0-9
* ...
*
* EX: int[] nums={8,1,4,2,23,10}; <
*
* 08,01,04,02,23,10
*
*
* 基数排序是稳定的排序算法.
* 基数排序的时间复杂度为O(d(n+r)),d为位数,r为基数.
*
*/
public static void radixSort(int[] nums) {
int max=nums[0];
for(int i=1;i<nums.length;i++){
if(max<nums[i])
max=nums[i];
}
int count=0;
do{
max=max/10;
count++;
}while(max!=0);
List<ArrayList<Integer>> digit = new ArrayList<ArrayList<Integer>>();
for (int i = 0; i < 10; i++) {
ArrayList queue = new ArrayList();
digit.add(queue);
}
for(int i=0;i<count;i++){
for(int j=0;j<nums.length;j++){
int add=(nums[j]%((int)Math.pow(10, i+1)))/((int)Math.pow(10, i));
digit.get(add).add(nums[j]);
}
int index = 0;
for(int j=0;j<10;j++){
while(digit.get(j).size()!=0){
nums[index++]=(int) digit.get(j).get(0);
digit.get(j).remove(0);
}
}
}
}
public static void main(String[] args) {
int[] nums={8,1,4,2,23,10};
bubbleSort(nums);//冒泡排序:Bubble Sort
quickSort(nums, 0, nums.length-1);//快速排序:Quick Sort
simpleSelectionSort(nums);//简单选择排序:Simple Selection Sort
heapSort(nums);//堆排序:Heap Sort
insertSort(nums);//直接插入排序:Straight Insertion Sort
binarySort(nums);//二分法插入排序:Binary Sort
shellSort(nums);//希尔排序:Shell Sort
mergeSort(nums,0,nums.length-1);//归并排序:Merge sort
radixSort(nums);//基数排序:Radix Sort
for(int i=0;i<nums.length;i++)
System.out.print(nums[i]+" ");
System.out.println();
}
}