java基本算法之排序

注:这个是以前写的,主要讲的是算法的思路,此文章的归并排序我并没有给出,因为当时还不会,而且没有基数排序和计数排序,最近重新学习了一遍,想要看的同学可以看我的另一篇文章:
http://blog.csdn.net/anny_lin/article/details/47722247

被数据结构虐的狗血淋头,于是乎要重头整理一遍,巩固一下知识块。以下算法的讲解均来自百度百科,代码是我自己打的,因为自己表述的可能不太清楚。。。

第一个:冒泡排序:重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

代码演示

private int[] bubbertSort(int[] a) {
        for(int i=0;i<a.length;i++)
            for(int j=0;j<a.length;j++)
                if (a[j]<a[i]) {
                    int temp=a[j];
                    a[j]=a[i];
                    a[i]=temp;

                }

第二个:直接插入排序:每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。
第一趟比较前两个数,然后把第二个数按大小插入到有序表中; 第二趟把第三个数据与前两个数从前向后扫描,把第三个数按大小插入到有序表中;依次进行下去,进行了(n-1)趟扫描以后就完成了整个排序过程。
直接插入排序属于稳定的排序,最坏时间复杂性为O(n^2),空间复杂度为O(1)。
直接插入排序是由两层嵌套循环组成的。外层循环标识并决定待比较的数值。内层循环为待比较数值确定其最终位置。直接插入排序是将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的。当前一数值比待比较数值大的情况下继续循环比较,直到找到比待比较数值小的并将待比较数值置入其后一位置,结束该次循环。

代码演示

private int[] directsSort(int[] a){
        int temp=0,flag=0;
        for (int i = 1; i < a.length; i++) 
        {
            int j=i-1;          
            temp=a[i];
            //一定是要j>=0不能是j>0,否则第一个比较会跳过
            for(;j>=0&&temp<a[j];j--){
                a[j+1]=a[j];
            }
            a[j+1]=temp;        
        }
        return a;
    }

第三个,简单选择排序,即依遍历得到a[n]中的最小值,然后进行排序的过程,遍历依次得到a[1]-a[n]最小值与[1]交换,然后遍历a[2]-a[n]最小值和a[2]交换,以此类推。

代码演示

private int[] simpleSort(int[] a) {
        int flag;
        for (int i = 0; i < a.length; i++) {
            flag=i;
            for (int j = i+1; j < a.length; j++) {
                if (a[j]<a[flag]) {
                    flag=j;
                }
            }
            if (i!=flag) {
             int temp=a[flag];
             a[flag]=a[i];
             a[i]=temp; 
            }               
        }
        return a;
    }

第四个:希尔排序
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

private int[] shellSort(int[] a) {
        //d为增量,每次取一半,直到等于1
        int i,j,d=0;
        for (d = a.length/2; d>0; d/=2) {
            for (i = 0; i < a.length; i++) {
                for(j=i-d;j>=0&&a[j]>a[j+d];j-=d){
                    int temp=a[j];
                     a[j]=a[j+d];
                     a[j+d]=temp;
                }
            }

        }

方法二:

public static void  main(String[] args) {
        int[] a =new int[20];
        for (int i = 0; i < a.length; i++) {
            a[i]=(int) (Math.random()*100);
        }
        int d=a.length/2;
        while (true) {
            for (int i = 0; i < d; i++) {
                for(int j=i;j+d<a.length;j+=d){
                    int temp;
                    if (a[j]>a[j+d]) {
                        temp=a[j];
                        a[j]=a[j+d];
                        a[j+d]=temp;
                    }
                }

            }
            if (d==1) {
                break;

            }
            d--;

        }
        for (int i = 0; i < a.length; i++) {
            System.out.println(a[i]);
        }

第五个:快速排序
设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序,快速排序不是一种稳定的排序算法。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]和A[i]互换;
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。

算法演示:
这个是网上的写法,目前还不太会,记录一下,以后再继续研究!!!!

    private void quickSort(int[] a,int low,int high) {
        if(low >= high)
        {
            return;
        }
        int first = low;
        int last = high;
        int key = a[first];/*用字表的第一个记录作为枢轴*/

        while(first < last)
        {
            while(first < last && a[last] >= key)
            {
                last--;
            }

            a[first] = a[last];/*将比第一个小的移到低端*/

            while(first < last && a[first] <= key)
            {
                first++;
            }

            a[last] = a[first];    /*将比第一个大的移到高端*/
        }
        a[first] = key;/*枢轴记录到位*/
        quickSort(a, low, first-1);
        quickSort(a, first+1, high);

    }

第六个:堆排序:(判断是否是堆可以判断是否是完全二叉树)
n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):
(1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2),当然,这是小根堆,大根堆则换成>=号。//k(i)相当于二叉树的非叶子结点,K(2i)则是左子节点,k(2i+1)是右子节点
若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:

树中任一非叶子结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
【例】关键字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分别满足堆性质(1)和(2),故它们均是堆,其对应的完全二叉树分别如小根堆示例和大根堆示例所示。
大根堆和小根堆:根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小根堆,又称最小堆。根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大根堆,又称最大堆。注意:①堆中任一子树亦是堆。②以上讨论的堆实际上是二叉堆(Binary Heap),类似地可定义k叉堆。

待补充

第七个:归并排序(二路归并排序)
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1;否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]。

public class MergeSort {
    /**
     * 归并排序
     * 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
     * 时间复杂度为O(nlogn)
     * 稳定排序方式
     * @param nums 待排序数组
     * @return 输出有序数组
     */
    public static int[] sort(int[] nums, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左边
            sort(nums, low, mid);
            // 右边
            sort(nums, mid + 1, high);
            // 左右归并
            merge(nums, low, mid, high);
        }
        return nums;
    }

    public static void merge(int[] nums, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];
        int i = low;// 左指针
        int j = mid + 1;// 右指针
        int k = 0;

        // 把较小的数先移到新数组中
        while (i <= mid && j <= high) {
            if (nums[i] < nums[j]) {
                temp[k++] = nums[i++];
            } else {
                temp[k++] = nums[j++];
            }
        }

        // 把左边剩余的数移入数组
        while (i <= mid) {
            temp[k++] = nums[i++];
        }

        // 把右边边剩余的数移入数组
        while (j <= high) {
            temp[k++] = nums[j++];
        }

        // 把新数组中的数覆盖nums数组
        for (int k2 = 0; k2 < temp.length; k2++) {
            nums[k2 + low] = temp[k2];
        }
    }


    // 归并排序的实现
    public static void main(String[] args) {

        int[] nums = { 2, 7, 8, 3, 1, 6, 9, 0, 5, 4 };

        MergeSort.sort(nums, 0, nums.length-1);
        System.out.println(Arrays.toString(nums));
    }
}

各个排序方法比较:
排序方法 平均时间 最坏时间 辅助存储
简单排序 O(n2) O(n2) O(1)
快速排序 O(nlogn) O(n2) O(logn)
堆排序 O(nlogn) O(nlogn) O(1)
归并排序 O(nlogn) O(nlogn) O(n)
基数排序 O(d(n+rd)) O(d(n+rd)) O(rd)
PS:直接插入排序、冒泡排序为简单排序
希尔排序、堆排序、快速排序为不稳定排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值