Chinaunix首页 | 论坛 | 博客
  • 博客访问: 44047
  • 博文数量: 15
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 176
  • 用 户 组: 普通用户
  • 注册时间: 2014-01-31 01:57
个人简介

资深码农

文章分类

全部博文(15)

文章存档

2016年(3)

2014年(12)

我的朋友

分类: C/C++

2014-01-31 11:55:04

给定一串无序的数字,如何找到从0开始算第K大的元素?听到这样一个问题你会有哪些思路?
这本是一道《编程珠玑》的练习题目,出自快速排序那一章,看到这你就应该把解这道题目和快速排序的做法联系在一起了。算法是这样的,在这串数字里找出个pivot,用来分割这串数字,经过O(n)时间复杂度之后,达到pivot左边的数字都小于(或等于)pivot,pivot右边都大于pivot。举例来说,给定int inputArray[] = {57, 354, 12, 3, 0, 99, 120, 2}; 使用57作为pivot,经过一次处理,得到
12,3,0,2,57,354,99,120。看到这里你会有一个问题,如果没有选57,而选0或者354作为pivot,那经过一次处理岂不是...? 对,我们要选出一个比较中等大小的数字作为pivot,可以考虑挑选一串数字中位置的第一个,最后一个和中间一个中第二大的数字来做pivot,即inputArray[left],inputArray[(left+right)/2]和inputArray[right]中的第二大的数做pivot。
选好了pivot,我们就可以开始下一步的工作了。选出pivot,看pivot是排第几个,如果正好是第K个,那就欧了,已经找到,算法结束;如果pivot是第j个,而j>k,那就是说第k个元素在pivot的左边,然后递归处理左侧的数据;如果j

点击(此处)折叠或打开

  1. #include "stdafx.h"
  2. #include "iostream"
  3. #include <string>

  4. using namespace std;

  5. static int swapCount = 0;

  6. void myswap(int& x1, int& x2)
  7. {
  8.     int temp = x2;
  9.     x2 = x1;
  10.     x1 = temp;
  11.     swapCount++;
  12. }

  13. void insertSort(int* arr, int left, int right)
  14. {
  15.     if (arr == 0)
  16.     {
  17.         string* NullPtrInput = new string("NULL POINTER in insertSort!");
  18.         throw NullPtrInput;
  19.     }
  20.     if (left >= right)
  21.     {
  22.         string* invalidInput = new string("left is larger than right in insertSort!");
  23.         throw invalidInput;
  24.     }

  25.     for (int i = 0; i <= right - left; i++)
  26.     {
  27.         for (int j = i; j > 0; j--)
  28.         {
  29.             if (arr[j+left] > arr[j-1+left])
  30.                 break;
  31.             else
  32.                 myswap(arr[j-1+left], arr[j+left]);
  33.         }
  34.     }
  35. }

  36. int qsort_k(int* arr, int left, int right, int k)
  37. {
  38.     if (!arr)
  39.     {
  40.         string* NullPtrInput = new string("NULL POINTER in qsort");
  41.         throw NullPtrInput;
  42.     }
  43.     if (left >= right)
  44.         return -1;

  45.     // find a middle number as the pivot
  46.     int mid = (left + right) / 2;
  47.     int tempArr[3] = {arr[left], arr[mid], arr[right]};
  48.     insertSort(tempArr, 0, 2);

  49.     if (tempArr[1] == arr[mid]) // find mid, place it in the left of the array
  50.     {
  51.         myswap(arr[left], arr[mid]);
  52.     }
  53.     else if (tempArr[1] == arr[right])
  54.     {
  55.         myswap(arr[left], arr[right]);
  56.     }

  57.     int swapnode = left+1;
  58.     bool hasNotFoundBiggerThanPivot = true;
  59.     for (int i = left + 1; i <= right; i++)
  60.     {
  61.         if (arr[left] > arr[i])
  62.         {
  63.             if (hasNotFoundBiggerThanPivot)
  64.             {
  65.                 swapnode++;
  66.                 continue;
  67.             }
  68.             myswap(arr[swapnode++], arr[i]);
  69.         }
  70.         else
  71.         {
  72.             if (hasNotFoundBiggerThanPivot)
  73.             {
  74.                 hasNotFoundBiggerThanPivot = false;
  75.                 continue;
  76.             }
  77.         }
  78.     }

  79.     int pivot = swapnode-1;
  80.     myswap(arr[left], arr[pivot]);

  81.     if (pivot == k)
  82.         return arr[pivot];
  83.     else if (pivot > k)
  84.         return qsort_k(arr, left, pivot-1, k);
  85.     else
  86.         return qsort_k(arr, pivot+1, right, k);
  87. }

  88. int _tmain(int argc, _TCHAR* argv[])
  89. {
  90.     
  91.     int arrayForSort[] = {545, 1, 57, 102, 60, 69, 274, 64, 32, 97, 10, 23, 78,
  92.                          21, 104, 265, 592, 312, 0, 122, 13, 56, 57, 993, 102,
  93.                          745, 43743, 3, 4556, 112, 367, 784, 52356, 5378, 987,
  94.                          66, 98, 421, 369, 787, 23, 98, 666, 671, 38, 2500, 9};

  95.     cout << "input array:" << endl;
  96.     for (int i = 0; i < sizeof(arrayForSort)/sizeof(int); i++)
  97.         cout << arrayForSort[i] << " ";
  98.     cout << endl;

  99.     int indexForSearch = 16; // start from zero
  100.     if (indexForSearch >= sizeof(arrayForSort)/sizeof(int))
  101.     {
  102.         cout << "indexForSearch out of bound, start from 0" << endl;
  103.         system("pause");
  104.         return 0;
  105.     }
  106.     int myfinding = qsort_k(arrayForSort, 0, sizeof(arrayForSort)/sizeof(int) - 1, indexForSearch);
  107.     cout << "The " << indexForSearch << "th (0 based) number is " << myfinding << endl;
  108.     cout << "swap count is " << swapCount << endl;
  109.     system("pause");
  110.     return 0;
  111. }
line 55:找pivot的时候,用一个插入排序来排3个数字,然后选出第二大的数。运行程序,有如下打印
input array:
545 1 57 102 60 69 274 64 32 97 10 23 78 21 104 265 592 312 0 122 13 56 57 993
02 745 43743 3 4556 112 367 784 52356 5378 987 66 98 421 369 787 23 98 666 671
8 2500 9
The 16th (0 based) number is 66
swap count is 67
可以看到,算法经过了67次交换,找到了第16大的元素是66。算法中递归的部分比较常规,而比较tricky的地方是如何经过O(n)把pivot的左边和右边分开,代码66到90行做了这个事,代码是我自己写的,当然不如大师写的那么简洁,不过工作起来效果是一样的。

阅读(783) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~