java

位置:IT落伍者 >> java >> 浏览文章

十大排序算法面试题


发布日期:2022年07月17日
 
十大排序算法面试题

选择排序
选择排序的基本思想是对待排序的记录序列进行n遍的处理第i遍处理是将L[in]中最小者与L[i]交换位置这样经过i遍处理之后前i个记录的位置已经是正确的了

选择排序是不稳定的算法复杂度是O(n ^ )
class SelectionSorter
{
    private int min;
    public void Sort(int[] arr)
    {
        for (int i = ; i < arr.Length - 1; ++i)
        {
            min = i;
            for (int j = i + 1; j < arr.Length; ++j)
            {
                if (arr[j] < arr[min])
                    min = j;
            }
            int t = arr[min];
            arr[min] = arr[i];
            arr[i] = t;
        }
    }
}
冒泡排序
冒泡排序方法是最简单的排序方法。tw.WiNgWIt.Com这种方法的基本思想是,将待排序的元素看作是竖着排列的“气泡”,较小的元素比较轻,从而要往上浮。在冒泡排序算法中我们要对这个“气泡”序列处理若干遍。所谓一遍处理,就是自底向上检查一遍这个序列,并时刻注意两个相邻的元素的顺序是否正确。如果发现两个相邻元素的顺序不对,即“轻”的元素在下面,就交换它们的位置。显然,处理一遍之后,“最轻”的元素就浮到了最高位置;处理二遍之后,“次轻”的元素就浮到了次高位置。在作第二遍处理时,由于最高位置上的元素已是“最轻”元素,所以不必检查。一般地,第i遍处理时,不必检查第i高位置以上的元素,因为经过前面i-1遍的处理,它们已正确地排好序。
冒泡排序是稳定的。算法时间复杂度是O(n ^2)
class EbullitionSorter
{
    public void Sort(int[] arr)
    {
        int i, j, temp;
        bool done = false;
        j = 1;
        while ((j < arr.Length) && (!done))//判断长度
        {
            done = true;
            for (i = 0; i < arr.Length - j; i++)
            {
                if (arr[i] > arr[i + 1])
                {
                    done = false;
                    temp = arr[i];
                    arr[i] = arr[i + 1];//交换数据
                    arr[i + 1] = temp;
                }
            }
            j++;
        }
    }
}

快速排序
快速排序是对冒泡排序的一种本质改进。它的基本思想是通过一趟扫描后,使得排序序列的长度能大幅度地减少。在冒泡排序中,一次扫描只能确保最大数值的数移到正确位置,而待排序序列的长度可能只减少1。快速排序通过一趟扫描,就能确保某个数(以它为基准点吧)的左边各数都比它小,右边各数都比它大。然后又用同样的方法处理它左右两边的数,直到基准点的左右只有一个元素为止。

快速排序是不稳定的。最理想情况算法时间复杂度O(nlog2n),最坏O(n ^2)。
class QuickSorter
{
    private void swap(ref int l, ref int r)
    {
        int temp;
        temp = l;
        l = r;
        r = temp;
    }
    public void Sort(int[] list, int low, int high)
    {
        int pivot;//存储分支点
        int l, r;
        int mid;
        if (high <= low)
            return;
        else if (high == low + 1)
        {
            if (list[low] > list[high])
                swap(ref list[low], ref list[high]);
            return;
        }
        mid = (low + high) >> 1;
        pivot = list[mid];
        swap(ref list[low], ref list[mid]);
        l = low + 1;
        r = high;
        do
        {
        while (l <= r && list[l] < pivot)
            l++;
        while (list[r] >= pivot)
            r–;
            if (l < r)
                swap(ref list[l], ref list[r]);
        } while (l < r);
        list[low] = list[r];
        list[r] = pivot;
        if (low + 1 < r)
            Sort(list, low, r - 1);
        if (r + 1 < high)
            Sort(list, r + 1, high);
    }
}   

插入排序
插入排序的基本思想是,经过i-1遍处理后,L[1..i-1]己排好序。第i遍处理仅将L[i]插入L[1..i-1]的适当位置,使得L[1..i]又是排好序的序列。要达到这个目的,我们可以用顺序比较的方法。首先比较L[i]和L[i-1],如果L[i-1]≤ L[i],则L[1..i]已排好序,第i遍处理就结束了;否则交换L[i]与L[i-1]的位置,继续比较L[i-1]和L[i-2],直到找到某一个位置j(1≤j≤i-1),使得L[j] ≤L[j+1]时为止。图1演示了对4个元素进行插入排序的过程,共需要(a),(b),(c)三次插入。
直接插入排序是稳定的。算法时间复杂度是O(n ^2) 。

public class InsertionSorter
{
    public void Sort(int[] arr)
    {
        for (int i = 1; i < arr.Length; i++)
        {
            int t = arr[i];
            int j = i;
            while ((j > 0) && (arr[j - 1] > t))
            {
                arr[j] = arr[j - 1];//交换顺序
                –j;
            }
            arr[j] = t;
        }
    }
}   

希尔排序
希尔排序基本思想: 先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2public class ShellSorter
{
    public void Sort(int[] arr)
    {
        int inc;
        for (inc = 1; inc <= arr.Length / 9; inc = 3 * inc + 1) ;
        for (; inc > 0; inc /= 3)
        {
            for (int i = inc + 1; i <= arr.Length; i += inc)
            {
                int t = arr[i - 1];
                int j = i;
                while ((j > inc) && (arr[j - inc - 1] > t))
                {
                    arr[j - 1] = arr[j - inc - 1];//交换数据
                    j -= inc;
                }
                arr[j - 1] = t;
            }
        }
    }
}   

归并排序
设有两个有序(升序)序列存储在同一数组中相邻的位置上,不妨设为A[l..m],A[m+1..h],将它们归并为一个有序数列,并存储在A[l..h]。

其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlog2n)。
///

        /// 归并排序之归:归并排序入口
        ///

        ///

无序的数组        /// 有序数组
        /// Lihua()
        int[] Sort(int[] data)
        {
            //取数组中间下标
            int middle = data.Length / 2;
            //初始化临时数组let,right,并定义result作为最终有序数组
            int[] left = new int[middle], right = new int[middle], result = new int[data.Length];
            if (data.Length % 2 != 0)//若数组元素奇数个,重新初始化右临时数组
            {
                right = new int[middle + 1];
            }
            if (data.Length <= 1)//只剩下1 or 0个元数,返回,不排序
            {
                return data;
            }
            int i = 0, j = 0;
            foreach (int x in data)//开始排序
            {
                if (i < middle)//填充左数组
                {
                    left[i] = x;
                    i++;
                }
                else//填充右数组
                {
                    right[j] = x;
                    j++;
                }
            }
            left = Sort(left);//递归左数组
            right = Sort(right);//递归右数组
            result = Merge(left, right);//开始排序
            //this.Write(result);//输出排序,测试用(lihua debug)
            return result;
        }
        ///

        /// 归并排序之并:排序在这一步
        ///

        ///

左数组        ///

右数组        /// 合并左右数组排序后返回
        int[] Merge(int[] a, int[] b)
        {
            //定义结果数组,用来存储最终结果
            int[] result = new int[a.Length + b.Length];
            int i = 0, j = 0, k = 0;
            while (i < a.Length && j < b.Length)
            {
                if (a[i] < b[j])//左数组中元素小于右数组中元素
                {
                    result[k++] = a[i++];//将小的那个放到结果数组
                }
                else//左数组中元素大于右数组中元素
                {
                    result[k++] = b[j++];//将小的那个放到结果数组
                }
            }
            while (i < a.Length)//这里其实是还有左元素,但没有右元素
            {
                result[k++] = a[i++];
            }
            while (j < b.Length)//右右元素,无左元素
            {
                result[k++] = b[j++];
            }
            return result;//返回结果数组
        }
注:此算法由周利华提供(

基数排序
基数排序法又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的比较性排序法。
//基数排序
        public int[] RadixSort(int[] ArrayToSort, int digit)
        {
            //low to high digit
            for (int k = 1; k <= digit; k++)
            {
                //temp array to store the sort result inside digit
                int[] tmpArray = new int[ArrayToSort.Length];
                //temp array for countingsort
                int[] tmpCountingSortArray = new int[10]{0,0,0,0,0,0,0,0,0,0};
                //CountingSort
                for (int i = 0; i < ArrayToSort.Length; i++)
                {
                    //split the specified digit from the element
                    int tmpSplitDigit = ArrayToSort[i]/(int)Math.Pow(10,k-1) - (ArrayToSort[i]/(int)Math.Pow(10,k))*10;
                    tmpCountingSortArray[tmpSplitDigit] += 1;
                }
                for (int m = 1; m < 10; m++)
                {
                    tmpCountingSortArray[m] += tmpCountingSortArray[m - 1];
                }
                //output the value to result
                for (int n = ArrayToSort.Length - 1; n >= 0; n–)
                {
                    int tmpSplitDigit = ArrayToSort[n] / (int)Math.Pow(10,k – 1) – (ArrayToSort[n]/(int)Math.Pow(10,k)) * 10;
                    tmpArray[tmpCountingSortArray[tmpSplitDigit]-1] = ArrayToSort[n];
                    tmpCountingSortArray[tmpSplitDigit] -= 1;
                }
                //copy the digit-inside sort result to source array
                for (int p = 0; p < ArrayToSort.Length; p++)
                {
                    ArrayToSort[p] = tmpArray[p];
                }
            }
            return ArrayToSort;
        }
计数排序
数排序, 基数排序, 桶排序等非比较排序算法,平均时间复杂度都是O(n). 这些排序因为其待排序元素本身就含有了定位特征,因而不需要比较就可以确定其前后位置,从而可以突破比较排序算法时间复杂度O(nlgn)的理论下限.
计数排序是最简单的特例,它要 求待排序元素是位于0到k之间的正整数 , 因而它是很特殊的情况,基本上没有特别的应用价值; 但是另一方面, 它又是基数排序的基础,或者说是一部分,所以简单的描述一下:
输入数组 A : 元素特征是 0-k的正整数,可以有重复值;
输出数组 B : 输出A的一个非减序列
中间数组 C : 大小是k, 它的i( 0<= i <= k)索引位置存储的是A元素集合中值是k的元素的个数有关.
算法的基本思想是:
统计A中元素的值的集合, 以A中元素的值为索引, 将值的个数填写到中间数组C的对应处.
对C从头开始自累加, 这样C中存储的就是, 当输入数组A中的值为当前索引时, 它前面的元素数量(包含重复元素).
将C依次输出到输出数组B中.
///

        /// counting sort
        ///

        ///

input array        ///

the value arrange in input array        ///
        public int[] CountingSort(int[] arrayA, int arrange)
        {
            //array to store the sorted result,
            //size is the same with input array.
            int[] arrayResult = new int[arrayA.Length];
            //array to store the direct value in sorting process
            //include index 0;
            //size is arrange+1;
            int[] arrayTemp = new int[arrange+1];
            //clear up the temp array
            for(int i = 0; i <= arrange; i++)
            {
                arrayTemp[i] = 0;
            }
            //now temp array stores the count of value equal
            for(int j = 0; j < arrayA.Length; j++)
            {
                arrayTemp[arrayA[j]] += 1;
            }
            //now temp array stores the count of value lower and equal
            for(int k = 1; k <= arrange; k++)
            {
                arrayTemp[k] += arrayTemp[k - 1];
            }
            //output the value to result
            for (int m = arrayA.Length-1; m >= 0; m–)
            {
                arrayResult[arrayTemp[arrayA[m]] – 1] = arrayA[m];
                arrayTemp[arrayA[m]] -= 1;
            }
            return arrayResult;
        }
根堆排序
堆排序是一种树形选择排序,在排序过程中,将A[n]看成是完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系来选择最小的元素。

堆排序是不稳定的。算法时间复杂度O(nlog n)。
///

        /// 小根堆排序
        ///

        ///

        ///

        ///

        private void HeapSort(ref double[] dblArray)
        {
            for (int i = dblArray.Length – 1; i >= 0; i–)
            {
                if (2 * i + 1 < dblArray.Length)
                {
                    int MinChildrenIndex = 2 * i + 1;
                    //比较左子树和右子树,记录最小值的Index
                    if (2 * i + 2 < dblArray.Length)
                    {
                        if (dblArray[2 * i + 1] > dblArray[2 * i + 2])
                            MinChildrenIndex = 2 * i + 2;
                    }
                    if (dblArray[i] > dblArray[MinChildrenIndex])
                    {

                        ExchageValue(ref dblArray[i], ref dblArray[MinChildrenIndex]);
                        NodeSort(ref dblArray, MinChildrenIndex);
                    }
                }
            }
        }
///

        /// 节点排序
        ///

        ///

        ///

        private void NodeSort(ref double[] dblArray, int StartIndex)
        {
            while (2 * StartIndex + 1 < dblArray.Length)
            {
                int MinChildrenIndex = 2 * StartIndex + 1;
                if (2 * StartIndex + 2 < dblArray.Length)
                {
                    if (dblArray[2 * StartIndex + 1] > dblArray[2 * StartIndex + 2])
                    {
                        MinChildrenIndex = 2 * StartIndex + 2;
                    }
                }
                if (dblArray[StartIndex] > dblArray[MinChildrenIndex])
                {
                    ExchageValue(ref dblArray[StartIndex], ref dblArray[MinChildrenIndex]);
                    StartIndex = MinChildrenIndex;
                }
            }
        }

        ///

        /// 交换值
        ///

        ///

        ///

        private void ExchageValue(ref double A, ref double B)
        {
            double Temp = A;
            A = B;
            B = Temp;
        }

               

上一篇:Java笔试试卷二

下一篇:介绍一下贪心算法的基本思路