查找最小的k 个元素之C#算法实现

紧接着上一篇微软编程面试100题,这次想解决的是查找最小的K个元素,题目是:输入n 个整数,输出其中最小的k 个。例如输入1,2,3,4,5,6,7 和8 这8 个数字,则最小的4 个数字为1,2,3 和4。

看到题目的时候我第一反应,这题很简单,使用任何方式的排序将数列按顺序存储,之后遍历需要的k个元素即可,于是自己动手很容易就完成了,但是后来在网络上发现很多人对这题的解决方式是用小根堆(MinHeap)或者大根堆(MaxHeap),这才意识到,其实出题人是醉翁之意不在酒,在乎复杂度的考虑也。

先写用排序的方式完成题目的方式吧,不仅简单,不需要费太多脑子,重要的是,正好趁这时候复习下排序,这里用快速排序完成:

        public static void Quick_Sort(int[] sort, int left, int right)
        {
            int mid = sort[(left + right) / 2];
            int i = left;
            int j = right;
            do
            {
                while (sort[i] < mid && i < right) i++;
                while (sort[j] > mid && j > left) j--;
                if (i <= j)
                {
                    int temp = sort[i];
                    sort[i] = sort[j];
                    sort[j] = temp;
                    i++;
                    j--;
                }

            } while (i <= j);

            if (j > left) Quick_Sort(sort, left, j);
            if (i < right) Quick_Sort(sort, i, right);
        }  

然后定义一个MinKMethod的方法来获取所需元素:

        public static void MinKMethod(int[] sort, int k)
        {
            Quick_Sort(sort, 0, sort.Length - 1);
            if (k > sort.Length)
            {
                for (int j = 0; j < sort.Length; j++)
                {
                    Console.Write(sort[j] + " ");
                }
                Console.WriteLine();
            }
            if (k <= 0)
            {
                Console.WriteLine("Nothing Output");
            }
            if (k > 0 && k < sort.Length)
            {
                for (int j = 0; j < k; j++)
                {
                    Console.Write(sort[j] + " ");
                }
            }

        } 

这么做的话,最快需要O(NlogN)的时间进行排序,然后在O(1)的时间内将k个数取出。

接下来看看如何用堆完成这个题:

        public static void FindKMin(int[] sort, int k)
        {
            int[] heap = sort;
            int rootIndex = k / 2 - 1;
            while (rootIndex >= 0)
            {
                reheap(heap, rootIndex, k - 1);
                rootIndex--;
            }

            for (int i = k, len=heap.Length; i < len; i++)
            {
                if (heap[i]<heap[0])
                {
                    heap[0] = heap[i];
                    reheap(heap, 0, k - 1);
                }
            }

            Console.WriteLine("The {0} min element =",k);
            for (int i = 0; i < k; i++)
            {
                Console.Write(heap[i] + " ");
            }
        }

        private static void reheap(int[] heap, int rootIndex, int lastInddex)
        {
            int orphan = heap[rootIndex];
            bool done = false;
            int leftIndex = rootIndex * 2 + 1;
            while (!done && leftIndex <= lastInddex)
            {
                int largerIndex = leftIndex;
                if (leftIndex+1 <= lastInddex)
                {
                    int rightIndex = leftIndex + 1;
                    if (heap[rightIndex] > heap[leftIndex])
                    {
                        largerIndex = rightIndex;
                    }
                }

                if (orphan < heap[largerIndex])
                {
                    heap[rootIndex] = heap[largerIndex];
                    rootIndex = largerIndex;
                    leftIndex = rootIndex * 2 + 1;
                }
                else
                {
                    done = true;
                }
            }

            heap[rootIndex] = orphan;
        }

用堆解决这个问题其实思路并不难,前提是,需要对堆有一定的理解。

posted @ 2014-03-29 13:57  Ribbon  阅读(1554)  评论(4编辑  收藏  举报