排序是程序开发中一种非常常见的操作,是对任意的一组数据经过排序操作后,就可以把他们变成一组按关键字排列的有序序列。对一个排序算法来说,一般从如下3个方面衡量算法的优劣:
时间复杂度:主要是分析关键字的比较次数和记录的移动次数。
空间复杂度:分析排序算法中需要多少辅助内存
稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种算法是稳定的;反之,就是不稳定的。
排序算法大致有直接插入排序、折半插入排序、Shell排序、归并排序、直接选择排序、堆排序、冒泡排序、快速排序、桶式排序、基数排序等这些种,各个算法都有其优异性,大家不妨自己看看。下面贴上每个算法的简单讲解和实现:
1.直接选择排序(DirectSelectSort):其关键就是对n个数据要进行n-1趟比较,每趟比较的目的就是选择出本趟比较中最小的数据,并将选择出的数据放在本趟中的第一位。实现如下:
public class DirectSelectSort {
//排序方法
public static void directSelectSort(int[] data){
int minData = 0;
int index = 0;
//进行n-1趟比较
for(int i=0; i<data.length-1; i++){
minData = data[i];
index = i;
for(int j=i+1; j<data.length; j++){
if(minData > data[j]){
minData = data[j];
index = j;
}
}
//一趟比较完后,交换一次
data[index] = data[i];
data[i] = minData;
}
}
public static void main(String args[]){
int data[] = {22,34,12,32,50,67,43,32};
System.out.print("排序前:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
System.out.println();
directSelectSort(data);
System.out.print("排序后:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
}
}
2.堆排序(HeapSort):先说下堆的概念,假设有n个数据元素的序列k0,k1,k2,k3,...,kn-1,当且仅当满足下列关系时,可以将这组数据称为小顶堆,即ki <= k2i+1且 ki<= k2i+2(其中i=0,2,4,...,(n-1)/2);或者,满足如下关系成为大顶堆,即ki >= k2i+1且 ki >= k2i+2(其中i=0,2,...,(n-1)/2)。如果将堆排成一棵完全二叉树,则小顶堆的特点是:树中所有节点的值都小于其左右节点的值,且根节点的值最小;而大顶堆相反。堆排序的关键在于:1.建堆(大顶堆或小顶堆)2.拿堆的根节点和最后一个节点交换。
public class HeapSort{
public static void heapSort(int[] data){
//循环建堆
for(int i=0; i<data.length-1; i++){
buildMaxHeap(data, data.length-1-i);
//交换栈顶和最后一个元素
swap(data, 0, data.length-1-i);
}
}
public static void buildMaxHeap(int[] data, int lastIndex){
//从最后一个节点的父节点开始
for(int i=(lastIndex-1)/2; i>=0; i--){
//保存当前正在判断的节点
int k = i;
//如果当前k节点的子节点存在
while(k*2+1 <= lastIndex){
int biggerIndex = 2*k+1;
if(biggerIndex < lastIndex){
if(data[biggerIndex] < data[biggerIndex+1]){
biggerIndex++;
}
}
if(data[k] < data[biggerIndex]){
//交换
swap(data, k, biggerIndex);
k = biggerIndex;
}else{
break;
}
}
}
}
public static void swap(int[] data, int i, int j){
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
public static void main(String[] args){
int data[] = {22,34,12,32,50,67,43,32};
System.out.print("排序前:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
System.out.println();
heapSort(data);
System.out.print("排序后:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
}
}
3. 冒泡排序(BubbleSort):冒泡排序是最简单的排序算法之一,实现起来也比较简单,其原理就是进行n-1趟比较并交换,小数往上冒,大数往下沉,经过n-1趟之后形成了有序的数列。
public class BubbleSort {
public static void bubbleSort(int[] data){
for(int i=0; i<data.length-1; i++){
for(int j=0; j<data.length-i-1; j++){
if(data[j] > data[j+1]){
int temp = data[j];
data[j] = data[j+1];
data[j+1] = temp;
}
}
}
}
public static void main(String[] args){
int data[] = {22,34,12,32,50,67,43,32};
System.out.print("排序前:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
System.out.println();
bubbleSort(data);
System.out.print("排序后:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
}
}
4.快速排序(QuickSort): 快速排序死对 冒泡排序的一种改进,基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
public class QuickSort {
public static void quickSort(int[] data, int start, int end){
if(start < end){
//以第一个元素为分界值
int middleNum = data[start];
int i = start;
int j = end + 1;
while(true){
//找到大于分界值的元素的索引或者已经到了end处
while(i<end && data[++i] <= middleNum);
//找到小于分界值的元素的索引或者已经到了start处
while(j>start && data[--j] >= middleNum);
if(i < j){
//交换
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}else{
break;
}
}
int temp = data[start];
data[start] = data[j];
data[j] = temp;
//递归左子序列
quickSort(data, start, j-1);
//递归右子序列
quickSort(data, j+1, end);
}
}
public static void main(String[] args){
int data[] = {22,34,12,32,50,67,43,32};
System.out.print("排序前:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
System.out.println();
quickSort(data, 0, data.length-1);
System.out.print("排序后:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
}
}
5.直接插入排序(DirectInsertSort):直接插入排序的思路很简单,就是依次将带排序的数据元素按其关键字排序的大小插入前面的有序序列。
public class DirectInsertSort {
public static void directInsertSort(int[] data){
for(int i=1; i<data.length; i++){
for(int j=0; j<i; j++){
if(data[i] < data[j]){
//保存插入元素
int temp = 0;
temp = data[i];
//将要插入元素位置后的元素依次往后移
for(int m=i; m>j; m--){
data[m] = data[m-1];
}
//插入元素
data[j] = temp;
}
}
}
}
public static void main(String[] args){
int data[] = {22,34,12,32,50,67,43,32};
System.out.print("排序前:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
System.out.println();
directInsertSort(data);
System.out.print("排序后:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
}
}
6.折半插入排序(BinaryInsertSort):折半插入排序是对直接插入排序的改进,具体操作为:在将一个新元素插入已排好序的数组的过程中,寻找插入点时,将待插入区域的首元素设置为a[low],末元素设置为a[high],则轮比较时将待插入元素与a[m],其中m=(low+high)/2相比较,如果比参考元素小,则选择a[low]到a[m-1]为新的插入区域(即high=m-1),否则选择a[m+1]到a[high]为新的插入区域(即low=m+1),如此直至low<=high不成立,即将此位置之后所有元素后移一位,并将新元素插入a[high+1]。
public class BinaryInsertSort {
public static void binaryInsertSort(int[] data){
for(int i=1; i<data.length; i++){
//保存要插入的数据
int temp = data[i];
int low = 0;
int high = i-1;
while(low <= high){
//找出low和high中间的索引
int mid = (low+high)/2;
if(temp > data[mid]){
//限制在大于mid的那一半搜索
low = mid + 1;
}else{
//限制在小于mid的那一半搜索
high = mid - 1;
}
}
//将low到i处的所有元素移位
for(int m=i; m>low; m--){
data[m] = data[m-1];
}
//插入元素
data[low] = temp;
}
}
public static void main(String[] args){
int data[] = {22,34,12,32,50,67,43,32};
System.out.print("排序前:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
System.out.println();
binaryInsertSort(data);
System.out.print("排序后:");
for(int i=0; i<data.length; i++){
System.out.print(data[i]+" ");
}
}
}
7.希尔排序(ShellSort):又称作缩小增量排序,其基本思想是:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插人排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。
public class ShellSort { public static void shellSort(int[] data){ int h = 1; //得到增量序列的最大值 while(h <= data.length-3){ h = h*3+1; } while(h > 0){ for(int i = h; i<data.length; i++){ int temp = data[i]; if(data[i] <= data[i-h]){ int j = i-h; for(; j >= 0 && data[j] > temp; j=j-h){ data[j+h] = data[j]; } //插入元素 data[j+h] = temp; } } h = (h-1)/3; } } public static void main(String[] args){ int data[] = {22,34,12,32,50,67,43,32}; System.out.print("排序前:"); for(int i=0; i<data.length; i++){ System.out.print(data[i]+" "); } System.out.println(); shellSort(data); System.out.print("排序后:"); for(int i=0; i<data.length; i++){ System.out.print(data[i]+" "); } } }
8.归并排序(MergeSort):其基本思想是将两个或两个以上的有序序列的集合合并成一个新的有序序列。这次我主要学习的还是将两个有序的有序序列合并成一个信的序列。细化来说,归并排序现将长度为n的无序序列看成是n个长度为1的有序子序列,首先做两两合并,得到n/2个长度为2的有序子序列,再做两两合并...不断重复这个过程,最终可以得到一个长度为n的有序序列。
public class MergeSort { public static void mergeSort(int[] data){ sort(data, 0, data.length - 1); } public static void sort(int[] data, int left, int right){ if (left < right){ //找出中间索引 int center = (left + right) / 2; //对左边数组进行递归 sort(data, left, center); //对右边数组进行递归 sort(data, center + 1, right); //合并 merge(data, left, center, right); } } private static void merge(int[] data , int left, int center, int right){ //定义一个与待排序序列长度相同的临时数组 int[] tmpArr = new int[data.length]; int mid = center + 1; //third记录中间数组的索引 int third = left; int tmp = left; while (left <= center && mid <= right){ //从两个数组中取出小的放入中间数组 if (data[left] <= data[mid]){ tmpArr[third++] = data[left++]; }else{ tmpArr[third++] = data[mid++]; } } //剩余部分依次放入中间数组 while (mid <= right){ tmpArr[third++] = data[mid++]; } while (left <= center){ tmpArr[third++] = data[left++]; } //将中间数组中的内容复制拷回原数组 //(原left~right范围的内容复制回原数组) while (tmp <= right){ data[tmp] = tmpArr[tmp++]; } } public static void main(String[] args){ int data[] = {22,34,12,32,50,67,43,32}; System.out.print("排序前:"); for(int i=0; i<data.length; i++){ System.out.print(data[i]+" "); } System.out.println(); mergeSort(data); System.out.print("排序后:"); for(int i=0; i<data.length; i++){ System.out.print(data[i]+" "); } } }
9.桶式排序(BucketSort):桶式排序不是基于比较的排序方法,这种排序需要满足一下两个特征:a.待排序列的所在值处于一个可枚举的范围之内;b.待排序列所在的这个可枚举范围不应该太大,否则排序的开销太大。其简单实现如下:
public class BucketSort { public static void bucketSort(int[] data, int min, int max){ //计数数组 int temp[] = new int[max]; for(int i=0; i<data.length; i++){ temp[data[i]]++; } for(int j=0; j<temp.length; j++){ if(temp[j]>0){ for(int m=0; m<temp[j]; m++){ data[min] = j; min++; } } } }
public static void main(String[] args){ int data[] = {3,5,2,6,4,7,3,9,1}; System.out.print("排序前:"); for(int i=0; i<data.length; i++){ System.out.print(data[i]+" "); } System.out.println(); bucketSort(data, 0, 10); System.out.print("排序后:"); for(int i=0; i<data.length; i++){ System.out.print(data[i]+" "); } } }
做完这些排序之后,心里感受还是颇多的,这才是正真的把数据结构的排序算法学了一遍,这才对排序有了一定的了解,当然,相信在以后的编程生涯中,将会有更多的体会的,动手才是硬道理,这里的排序只是一个简单的实现,以后有时间的话再说说它们的时间复杂度和空间复杂度以及稳定性什么的!