算法练手 快排,堆排序,插入排序, 二分查找

排序,重要性不言而喻。 今天练手了快排,堆排序,和插入排序, 二分查找。 快排参考了 http://blogread.cn/it/article/612?f=sr ,写的不错! 可以看看。

先总结下: 边界值很重要,一定要考虑到。

说下快排,这个排序自诞生起,就引起了轰动,也名列十大经典算法之一。它主要分为两个步骤,划分和递归, 其中划分是算法的核心,递归是重要的思想。递归部分伪代码如下:

1 quicksort(A[], p, r)
2    if p < r
3        q = partition(a, p, r)
4        quicksort(a, p, q-1)
5        quicksort(a, q+1, r)

划分,就是选取一个主元(中间数), 把小的放在它左边,大的放在它右边; 接着递归左边,右边,直到条件终止退出。盗用下算法导论中的图,划分就是把区间分为四块,小于,大于,未定和主元。

 

主元的选取一般来说有三种: 两端,随机,三数取中, 这里我们使用了是两端(尾端),先实现下算法导论中的方法:

 1 template<class T>
 2 int partition_ia(T a[], int beg, int last)
 3 {
 4     T piot = a[last];
 5     int i = beg-1;
 6     for(int j=beg; j<last; ++j){
 7         if(a[j] > piot)
 8             swap(a[++i], a[j]);
 9     }
10     swap(a[++i], a[last]); 
11     return i;
12 }

这种方法貌似不是很好,在前面给的链接里,提出了一个从两端开始的方法:

template<class T>
int partition_double(T a[], int beg, int last)
{
    T piot = a[last];
    int i = beg-1;
    int j = last;
    while(1){
       do ++i; while(i<=last && a[i] <= piot);
       do --j; while(a[j]>piot);
       if(i>j) break;
       swap(a[i], a[j]);
    }
    swap(a[i], a[last]);
return i;
 }

另外,还有改进的双端扫描,采用尾递归手段减少栈的大小等等。

现在说下堆排序,之前的博文中用类实现堆排序,现在使用函数形式实现

int heap_size = 0;
template<class T>
void maxheapify(T a[], int index)
{
    int l = index*2+1;
    int r = (index+1)*2;
    int large = index;
    if(l < heap_size){
        if(a[l] > a[index]) large = l;
    }
    if(r < heap_size){
        if(a[r] > a[large])  large = r;
    }
    if(index != large) {
        swap(a[large], a[index]);
        maxheapify(a, large);
    }
}
template<class T>
void build_heap(T a[], int len)
{
     heap_size = len;
     for(int i=(heap_size-1)/2; i>=0; --i){
         maxheapify(a, i);
    }
}
template<class T>
void heapsort(T a[], int len)
{
    for(int i=1; i<len; ++i){
         swap(a[0], a[--heap_size]);
         maxheapify(a, 0);
    }
}

插入排序:  这个思想很经典,都是拿扑克牌举例的。。我们可以这么认为,数组被分为两部分,一部分是已排好的,另一部分是乱序的; 每次排序从乱序中拿出一张(头), 插入到合适位置。

template<class T>
void insertsort(T a[], int len)
{
      T key;
       int j, i;
      for(ii=1; i<len; ++i){
          key = a[i];
          for(j=i-1; j>=0; --j){
              if(a[j]<=key) break;
              a[j+1] = a[j];
          }
          a[j+1] = key;
     }
}

二分查找:  有句话说,90%的程序员一次写不对二分查找。。我试了下,我也是属于90%的。

二分查找有两种解题思路,一种是递归,一种是循环。参考文章: http://www.kuqin.com/algorithm/20120911/330495.html

先说递归,这个思想好理解:

 1 template<class T>
 2 int bisearch_d(T a[], int beg, int last, const T& x)
 3 {
 4      int mid = beg + (last-beg)/2;
 5      if(mid<=last){
 6          if(a[mid] == x) return mid+1;
 7          else if(a[mid]>x) return bisearch_d(a, beg, mid-1,x);
 8          else return bisearch_d(a,mid+1, last, x);
 9      }
10      return 0;
11 }

这个可以用循环很简单的实现

template<class T>
void bisearch_cy(T a[], int low, int high, const T& x)
{
      int mid = low + (high-low)/2;
      while(low <= high){
           if(a[mid-1] == x) return mid;
           else if(a[mid-1] > x) high = mid-1;
           else low = mid + 1;
           mid = low + (high-low) / 2;
      }
      return 0;
}
posted @ 2013-05-31 22:43  xield  阅读(441)  评论(1编辑  收藏  举报