• Java常用算法总结


    交换排序
    冒泡排序
    将最后一个元素与倒数第二个元素对比,如果最后一个元素比倒数第二个小,则交换两个元素的位置,再用倒数第二个元素与倒数第三个元数对比,直到比到第一个元素,这样经过第一趟排序后得到第一个最小元素。如此反复几过N(N=length-1)次后可得到排序结果。 
    
    Java代码  收藏代码
    package sort; 
     
    import java.util.Comparator; 
     
    /**
    * 冒泡排序算法
    * @author jzj
    * @date 2009-12-9
    *
    * @param <E>
    */ 
    publicclass BubbleSort<E extends Comparable<E>> extends Sort<E> { 
     
        /**
         * 排序算法的实现,对数组中指定的元素进行排序
         * @param array 待排序的数组
         * @param from 从哪里开始排序
         * @param end 排到哪里
         * @param c 比较器
         */ 
        publicvoid sort(E[] array, int from, int end, Comparator<E> c) { 
            //需array.length - 1轮比较 
            for (int k = 1; k < end - from + 1; k++) { 
                //每轮循环中从最后一个元素开始向前起泡,直到i=k止,即i等于轮次止 
                for (int i = end - from; i >= k; i--) { 
                    //按照一种规则(后面元素不能小于前面元素)排序 
                    if (c.compare(array[i], array[i - 1]) < 0) { 
                        //如果后面元素小于了(当然是大于还是小于要看比较器实现了)前面的元素,则前后交换 
                        swap(array, i, i - 1); 
                    } 
                } 
            } 
        } 
     
        /**
        * 测试
        * @param args
        */ 
        publicstaticvoid main(String[] args) { 
            Integer[] intgArr = { 7, 2, 4, 3, 12, 1, 9, 6, 8, 5, 11, 10 }; 
            BubbleSort<Integer> sort = new BubbleSort<Integer>(); 
            BubbleSort.testSort(sort, intgArr); 
            BubbleSort.testSort(sort, null); 
        } 
    } 
    快速排序
    快速排序采用了分治法的思想,把大的问题分解为同类型的小问题。 一般分如下步骤: 1)选择一个中枢元素(有很多选法,我的实现里使用第一个元素为中枢的简单方法) 2)以该中枢元素为基准点,将小于中枢的元素放在中枢后集合的前部分,比它大的在集合后部分,待集合基本排序完成后(此时前部分元素小于后部分元素),把中枢元素放在合适的位置。 3)根据中枢元素最后确定的位置,把数组分成三部分,左边的,右边的,枢纽元素自己,对左边的,右边的分别递归调用快速排序算法即可。
    这里的重点与难点在于第二步,实现的方式有很多种,我这里实现了三种。 第一种实现(partition1方法): 以第一个元素为中枢元素,在中枢元素后面集合中从前往后寻找第一个比中枢元素小的元素,并与第一个元素交换,然后从剩余的元素中寻找第二个比中枢元素小的 元素,并与第二位元素交换,这样直到所有小于中枢元素找完为止,并记下最后一次放置小于中枢的元素位置minIndex(即小于中枢与大于中枢的分界), 并将中枢元素与minIndex位置元素互换,然后对中枢元素两边的序列进行同样的操作。 此种实现最为简洁,处理过程中不需要把中枢元素移来移去,只是在其它元素完成基本排序后(前部分小于后部分元素)再把中枢元素放置到适当的位置  第二种实现(partition2方法): 以第一个元素为中枢元素,刚开始时使用低指针指向中枢元素。当中枢元素在低指针位置时,此时我们判断高指针指向的元素是否小于中枢元素,如果大于中枢元素 则高指针继续向头移动,如果小于则与中枢元素交换,此时中枢元素被移到了高指针位置;当中枢元素在高指针位置时,我们此时判断低指针指向的元素是否大于中 枢元素,如果小于中枢元素则低指针继续向尾移动,如果大于则与中枢元素交换,此时中枢元素又回到了低指针位置;这时是拿高还是低指针所指向的元素与中枢比 较时根据前面逻辑来处理,直到高低指针指向同一位置则完成一轮排序,然后再对中枢元素两边的序列进行同样的操作直到排序完成 此种实现逻辑比较好理解,中枢元素的永远在低指针或指针所指向的位置,每次找到需处理的元 素后,要与中枢交换,中枢就像皮球一样从这里踢到那里,又从那里踢到这里。但此种实现会频繁地交换中枢元素,性能可能不如第一种  第三种实现(partition3方法): 此种方式与前两种方式不太一样,同时移动高低指针,低指针向尾找出大于等于中枢的元素,而高向头找出小于中枢的元素,待两者都找出后交换高低指针所指向的 元素,直到高低指针指向同一位置止,然后比较中枢与高低指针所指向的元素大小,如果中枢元素大,则直接与高低指针元素交换,如果中枢元素小于等于高低指针 元素,则中枢元素与高低指针前一元素交换,完成一轮比较,然后再对中枢元素两边的序列进行同样的操作直到排序完成
    
    此种方式有点难度,在移动元素时要注意的是:与中枢相等的元素也要向集合后部移动,不然的话如[3,3,0,3,3]第一轮排序结果不准确,虽然最后结果 正确。当中枢后面的元素集合移动完成后,还得要把中枢元素放置在集合中的合适位置,这就需要找准集合中前部分与后部分的边界,最后只能把中枢元素与最后一 个小于中枢的元素进位置互换。但此种实现方式与第一种有点像,也不需要把中枢元素调来调去的,而是待后面集合排序完成后将中枢放入适当位置 
    
    Java代码  收藏代码
    package sort; 
     
    import java.util.Arrays; 
    import java.util.Comparator; 
     
    /**
    * 快速排序算法
    * @author jzj
    * @date 2009-12-9
    *
    * @param <E>
    */ 
    publicclass QuickSort<E extends Comparable<E>> extends Sort<E> { 
     
        /**
         * 排序算法的实现,对数组中指定的元素进行排序
         * @param array 待排序的数组
         * @param from 从哪里开始排序
         * @param end 排到哪里
         * @param c 比较器
         */ 
        publicvoid sort(E[] array, int from, int end, Comparator<E> c) { 
            quickSort(array, from, end, c); 
        } 
     
        /**
         * 递归快速排序实现
         * @param array 待排序数组
         * @param low 低指针
         * @param high 高指针
         * @param c 比较器
         */ 
        privatevoid quickSort(E[] array, int low, int high, Comparator<E> c) { 
            /*
             * 如果分区中的低指针小于高指针时循环;如果low=higth说明数组只有一个元素,无需再处理;
             * 如果low > higth,则说明上次枢纽元素的位置pivot就是low或者是higth,此种情况
             * 下分区不存,也不需处理
             */ 
            if (low < high) { 
                //对分区进行排序整理 
                int pivot = partition1(array, low, high, c); 
                /*
                 * 以pivot为边界,把数组分成三部分[low, pivot - 1]、[pivot]、[pivot + 1, high]
                 * 其中[pivot]为枢纽元素,不需处理,再对[low, pivot - 1]与[pivot + 1, high]
                 * 各自进行分区排序整理与进一步分区
                 */ 
                quickSort(array, low, pivot - 1, c); 
                quickSort(array, pivot + 1, high, c); 
            } 
     
        } 
     
        /**
         * 实现一
         *
         * @param array 待排序数组
         * @param low 低指针
         * @param high 高指针
         * @param c 比较器
         * @return int 调整后中枢位置
         */ 
        privateint partition1(E[] array, int low, int high, Comparator<E> c) { 
            E pivotElem = array[low];//以第一个元素为中枢元素 
            //从前向后依次指向比中枢元素小的元素,刚开始时指向中枢,也是小于与大小中枢的元素的分界点 
            int border = low; 
     
            /*
             * 在中枢元素后面的元素中查找小于中枢元素的所有元素,并依次从第二个位置从前往后存放
             * 注,这里最好使用i来移动,如果直接移动low的话,最后不知道数组的边界了,但后面需要
             * 知道数组的边界
             */ 
            for (int i = low + 1; i <= high; i++) { 
                //如果找到一个比中枢元素小的元素 
                if (c.compare(array[i], pivotElem) < 0) { 
                    swap(array, ++border, i);//border前移,表示有小于中枢元素的元素 
                } 
            } 
            /*
             * 如果border没有移动时说明说明后面的元素都比中枢元素要大,border与low相等,此时是
             * 同一位置交换,是否交换都没关系;当border移到了high时说明所有元素都小于中枢元素,此
             * 时将中枢元素与最后一个元素交换即可,即low与high进行交换,大的中枢元素移到了 序列最
             * 后;如果 low <minIndex< high,表 明中枢后面的元素前部分小于中枢元素,而后部分大于
             * 中枢元素,此时中枢元素与前部分数组中最后一个小于它的元素交换位置,使得中枢元素放置在
             * 正确的位置
             */ 
            swap(array, border, low); 
            return border; 
        } 
     
        /**
         * 实现二
         *
         * @param array 待排序数组
         * @param low 待排序区低指针
         * @param high 待排序区高指针
         * @param c 比较器
         * @return int 调整后中枢位置
         */ 
        privateint partition2(E[] array, int low, int high, Comparator<E> c) { 
            int pivot = low;//中枢元素位置,我们以第一个元素为中枢元素 
            //退出条件这里只可能是 low = high 
            while (true) { 
                if (pivot != high) {//如果中枢元素在低指针位置时,我们移动高指针 
                    //如果高指针元素小于中枢元素时,则与中枢元素交换 
                    if (c.compare(array[high], array[pivot]) < 0) { 
                        swap(array, high, pivot); 
                        //交换后中枢元素在高指针位置了 
                        pivot = high; 
                    } else {//如果未找到小于中枢元素,则高指针前移继续找 
                        high--; 
                    } 
                } else {//否则中枢元素在高指针位置 
                    //如果低指针元素大于中枢元素时,则与中枢元素交换 
                    if (c.compare(array[low], array[pivot]) > 0) { 
                        swap(array, low, pivot); 
                        //交换后中枢元素在低指针位置了 
                        pivot = low; 
                    } else {//如果未找到大于中枢元素,则低指针后移继续找 
                        low++; 
                    } 
                } 
                if (low == high) { 
                    break; 
                } 
            } 
            //返回中枢元素所在位置,以便下次分区 
            return pivot; 
        } 
     
        /**
         * 实现三
         *
         * @param array 待排序数组
         * @param low 待排序区低指针
         * @param high 待排序区高指针
         * @param c 比较器
         * @return int 调整后中枢位置
         */ 
        privateint partition3(E[] array, int low, int high, Comparator<E> c) { 
            int pivot = low;//中枢元素位置,我们以第一个元素为中枢元素 
            low++; 
            //----调整高低指针所指向的元素顺序,把小于中枢元素的移到前部分,大于中枢元素的移到后面部分 
            //退出条件这里只可能是 low = high 
     
            while (true) { 
                //如果高指针未超出低指针 
                while (low < high) { 
                    //如果低指针指向的元素大于或等于中枢元素时表示找到了,退出,注:等于时也要后移 
                    if (c.compare(array[low], array[pivot]) >= 0) { 
                        break; 
                    } else {//如果低指针指向的元素小于中枢元素时继续找 
                        low++; 
                    } 
                } 
     
                while (high > low) { 
                    //如果高指针指向的元素小于中枢元素时表示找到,退出 
                    if (c.compare(array[high], array[pivot]) < 0) { 
                        break; 
                    } else {//如果高指针指向的元素大于中枢元素时继续找 
                        high--; 
                    } 
                } 
                //退出上面循环时 low = high 
                if (low == high) { 
                    break; 
                } 
     
                swap(array, low, high); 
            } 
     
            //----高低指针所指向的元素排序完成后,还得要把中枢元素放到适当的位置 
            if (c.compare(array[pivot], array[low]) > 0) { 
                //如果退出循环时中枢元素大于了低指针或高指针元素时,中枢元素需与low元素交换 
                swap(array, low, pivot); 
                pivot = low; 
            } elseif (c.compare(array[pivot], array[low]) <= 0) { 
                swap(array, low - 1, pivot); 
                pivot = low - 1; 
            } 
     
            //返回中枢元素所在位置,以便下次分区 
            return pivot; 
        } 
     
        /**
        * 测试
        * @param args
        */ 
        publicstaticvoid main(String[] args) { 
            Integer[] intgArr = { 3, 1, 1, 1, 1, 1, 1 }; 
            QuickSort<Integer> sort = new QuickSort<Integer>(); 
            QuickSort.testSort(sort, intgArr); 
            QuickSort.testSort(sort, null); 
        } 
    }  
    归并排序
    
    
    Java代码  收藏代码
    package sort; 
     
    import java.lang.reflect.Array; 
    import java.util.Comparator; 
     
    /**
    * 归并排序算法
    * @author jzj
    * @date 2009-12-11
    *
    * @param <E>
    */ 
    publicclass MergeSort<E extends Comparable<E>> extends Sort<E> { 
     
        /**
         * 排序算法的实现,对数组中指定的元素进行排序
         * @param array 待排序的数组
         * @param from 从哪里开始排序
         * @param end 排到哪里
         * @param c 比较器
         */ 
        publicvoid sort(E[] arr, int from, int end, Comparator<E> c) { 
            partition(arr, from, end, c); 
        } 
     
        /**
         * 递归划分数组
         * @param arr
         * @param from
         * @param end
         * @param c void
         */ 
        privatevoid partition(E[] arr, int from, int end, Comparator<E> c) { 
            //划分到数组只有一个元素时才不进行再划分 
            if (from < end) { 
                //从中间划分成两个数组 
                int mid = (from + end) / 2; 
                partition(arr, from, mid, c); 
                partition(arr, mid + 1, end, c); 
                //合并划分后的两个数组 
                merge(arr, from, end, mid, c); 
            } 
        } 
     
        /**
         * 数组合并,合并过程中对两部分数组进行排序
         * 前后两部分数组里是有序的
         * @param arr
         * @param from
         * @param end
         * @param mid
         * @param c void
         */ 
        privatevoid merge(E[] arr, int from, int end, int mid, Comparator<E> c) { 
            E[] tmpArr = (E[]) Array.newInstance(arr[0].getClass(), end - from + 1); 
            int tmpArrIndex = 0;//指向临时数组 
            int part1ArrIndex = from;//指向第一部分数组 
            int part2ArrIndex = mid + 1;//指向第二部分数组 
     
            //由于两部分数组里是有序的,所以每部分可以从第一个元素依次取到最后一个元素,再对两部分 
            //取出的元素进行比较。只要某部分数组元素取完后,退出循环 
            while ((part1ArrIndex <= mid) && (part2ArrIndex <= end)) { 
                //从两部分数组里各取一个进行比较,取最小一个并放入临时数组中 
                if (c.compare(arr[part1ArrIndex], arr[part2ArrIndex]) < 0) { 
                    //如果第一部分数组元素小,则将第一部分数组元素放入临时数组中,并且临时数组指针 
                    //tmpArrIndex下移一个以做好下次存储位置准备,前部分数组指针part1ArrIndex 
                    //也要下移一个以便下次取出下一个元素与后部分数组元素比较 
                    tmpArr[tmpArrIndex++] = arr[part1ArrIndex++]; 
                } else { 
                    //如果第二部分数组元素小,则将第二部分数组元素放入临时数组中 
                    tmpArr[tmpArrIndex++] = arr[part2ArrIndex++]; 
                } 
            } 
            //由于退出循环后,两部分数组中可能有一个数组元素还未处理完,所以需要额外的处理,当然不可 
            //能两部分数组都有未处理完的元素,所以下面两个循环最多只有一个会执行,并且都是大于已放入 
            //临时数组中的元素 
            while (part1ArrIndex <= mid) { 
                tmpArr[tmpArrIndex++] = arr[part1ArrIndex++]; 
            } 
            while (part2ArrIndex <= end) { 
                tmpArr[tmpArrIndex++] = arr[part2ArrIndex++]; 
            } 
     
            //最后把临时数组拷贝到源数组相同的位置 
            System.arraycopy(tmpArr, 0, arr, from, end - from + 1); 
        } 
     
        /**
         * 测试
         * @param args
         */ 
        publicstaticvoid main(String[] args) { 
            Integer[] intgArr = { 5, 9, 1, 4, 1, 2, 6, 3, 8, 0, 7 }; 
            MergeSort<Integer> insertSort = new MergeSort<Integer>(); 
            Sort.testSort(insertSort, intgArr); 
            Sort.testSort(insertSort, null); 
        } 
    }  
    基数排序
    基数排序的主要思路是,将所有待比较数值(注意,必须是正整数)统一为同样的数位长度,数位较短的数前面补零. 然后, 从最低位开始, 依次进行一次稳定排序.这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列.
    
    它的理论比较容易理解,但实现却有一点绕。
    
    
    
    Java代码  收藏代码
    package sort; 
     
    import java.util.Arrays; 
     
    publicclass RadixSort { 
     
        /**
         * 取数x上的第d位数字
         * @param x 数
         * @param d 第几位,从低位到高位
         * @return
         */ 
        publicint digit(long x, long d) { 
     
            long pow = 1; 
            while (--d > 0) { 
                pow *= 10; 
            } 
            return (int) (x / pow % 10); 
        } 
     
        /**
         * 基数排序实现,以升序排序(下面程序中的位记录器count中,从第0个元素到第9个元素依次用来
         * 记录当前比较位是0的有多少个..是9的有多少个数,而降序时则从第0个元素到第9个元素依次用来
         * 记录当前比较位是9的有多少个..是0的有多少个数)
         * @param arr 待排序数组
         * @param digit 数组中最大数的位数
         * @return
         */ 
        publiclong[] radixSortAsc(long[] arr) { 
            //从低位往高位循环 
            for (int d = 1; d <= getMax(arr); d++) { 
                //临时数组,用来存放排序过程中的数据 
                long[] tmpArray = newlong[arr.length]; 
                //位记数器,从第0个元素到第9个元素依次用来记录当前比较位是0的有多少个..是9的有多少个数 
                int[] count = newint[10]; 
                //开始统计0有多少个,并存储在第0位,再统计1有多少个,并存储在第1位..依次统计到9有多少个 
                for (int i = 0; i < arr.length; i++) { 
                    count[digit(arr[i], d)] += 1; 
                } 
                /*
                 * 比如某次经过上面统计后结果为:[0, 2, 3, 3, 0, 0, 0, 0, 0, 0]则经过下面计算后 结果为:
                 * [0, 2, 5, 8, 8, 8, 8, 8, 8, 8]但实质上只有如下[0, 2, 5, 8, 0, 0, 0, 0, 0, 0]中
                 * 非零数才用到,因为其他位不存在,它们分别表示如下:2表示比较位为1的元素可以存放在索引为1、0的
                 * 位置,5表示比较位为2的元素可以存放在4、3、2三个(5-2=3)位置,8表示比较位为3的元素可以存放在
                 * 7、6、5三个(8-5=3)位置
                 */ 
                for (int i = 1; i < 10; i++) { 
                    count[i] += count[i - 1]; 
                } 
     
                /*
                 * 注,这里只能从数组后往前循环,因为排序时还需保持以前的已排序好的 顺序,不应该打
                 * 乱原来已排好的序,如果从前往后处理,则会把原来在前面会摆到后面去,因为在处理某个
                 * 元素的位置时,位记数器是从大到到小(count[digit(arr[i], d)]--)的方式来处
                 * 理的,即先存放索引大的元素,再存放索引小的元素,所以需从最后一个元素开始处理。
                 * 如有这样的一个序列[212,213,312],如果按照从第一个元素开始循环的话,经过第一轮
                 * 后(个位)排序后,得到这样一个序列[312,212,213],第一次好像没什么问题,但问题会
                 * 从第二轮开始出现,第二轮排序后,会得到[213,212,312],这样个位为3的元素本应该
                 * 放在最后,但经过第二轮后却排在了前面了,所以出现了问题
                 */ 
                for (int i = arr.length - 1; i >= 0; i--) {//只能从最后一个元素往前处理 
                    //for (int i = 0; i < arr.length; i++) {//不能从第一个元素开始循环 
                    tmpArray[count[digit(arr[i], d)] - 1] = arr[i]; 
                    count[digit(arr[i], d)]--; 
                } 
     
                System.arraycopy(tmpArray, 0, arr, 0, tmpArray.length); 
            } 
            return arr; 
        } 
     
        /**
         * 基数排序实现,以降序排序(下面程序中的位记录器count中,从第0个元素到第9个元素依次用来
         * 记录当前比较位是0的有多少个..是9的有多少个数,而降序时则从第0个元素到第9个元素依次用来
         * 记录当前比较位是9的有多少个..是0的有多少个数)
         * @param arr 待排序数组
         * @return
         */ 
        publiclong[] radixSortDesc(long[] arr) { 
            for (int d = 1; d <= getMax(arr); d++) { 
                long[] tmpArray = newlong[arr.length]; 
                //位记数器,从第0个元素到第9个元素依次用来记录当前比较位是9的有多少个..是0的有多少个数 
                int[] count = newint[10]; 
                //开始统计0有多少个,并存储在第9位,再统计1有多少个,并存储在第8位..依次统计 
                //到9有多少个,并存储在第0位 
                for (int i = 0; i < arr.length; i++) { 
                    count[9 - digit(arr[i], d)] += 1; 
                } 
     
                for (int i = 1; i < 10; i++) { 
                    count[i] += count[i - 1]; 
                } 
     
                for (int i = arr.length - 1; i >= 0; i--) { 
                    tmpArray[count[9 - digit(arr[i], d)] - 1] = arr[i]; 
                    count[9 - digit(arr[i], d)]--; 
                } 
     
                System.arraycopy(tmpArray, 0, arr, 0, tmpArray.length); 
            } 
            return arr; 
        } 
     
        privateint getMax(long[] array) { 
            int maxlIndex = 0; 
            for (int j = 1; j < array.length; j++) { 
                if (array[j] > array[maxlIndex]) { 
                    maxlIndex = j; 
                } 
            } 
            return String.valueOf(array[maxlIndex]).length(); 
        } 
     
        publicstaticvoid main(String[] args) { 
            long[] ary = newlong[] { 123, 321, 132, 212, 213, 312, 21, 223 }; 
            RadixSort rs = new RadixSort(); 
            System.out.println("升 - " + Arrays.toString(rs.radixSortAsc(ary))); 
     
            ary = newlong[] { 123, 321, 132, 212, 213, 312, 21, 223 }; 
            System.out.println("降 - " + Arrays.toString(rs.radixSortDesc(ary))); 
        } 
    } 
    时间复杂度与空间复杂度对比表
     
  • 相关阅读:
    xslt转换xml常用知识(4)
    xslt转换xml
    Oracle10g数据库的4种存储形式 .转帖
    建立与Oracle数据库服务器连接的两种连接模式(专用服务器与共享服务器) .转帖
    HDU 4473 Exam 构造枚举
    zoj 1002 Fire Net (DFS搜索)
    POJ 2421 Constructing Roads 克鲁斯卡尔(Kruskal)算法
    POJ 1258 AgriNet 克鲁斯卡尔(Kruskal)算法&并查集
    POJ 2031 Building a Space Station
    判断一个数是否为整数(转)
  • 原文地址:https://www.cnblogs.com/xianz666/p/13554936.html
Copyright © 2020-2023  润新知