Chinaunix首页 | 论坛 | 博客
  • 博客访问: 234936
  • 博文数量: 59
  • 博客积分: 4010
  • 博客等级: 上校
  • 技术积分: 900
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-30 20:21
文章分类

全部博文(59)

文章存档

2011年(1)

2009年(58)

我的朋友

分类: C/C++

2009-04-14 21:52:41

分治法的基本思想是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相同。递归地解这些子问题,然后将各子问题的解合并得到原问题的解。其一般的算法设计模式如下:

Divide-and-Conquer(P)
{
   if (|P|<=n0) Adhoc(P):
   divide P into smaller subinstance
   P1, P2,...,Pk
   for (i=0; i    {
       yi = Divide-and-Conquer(Pi);
    }
    return Merge(y1,y2,...,yk);
}
许多问题可以取k=2,这种使子问题规模大致相等的做法是出自一种平衡子问题的思想,它几乎总比子问题规模不等的做法要好。

二分搜索算法是运用分治策略的一个典型例子。
#include

int BinarySearch( int *arr, size_t n, int elem )
{
  size_t left = 0;
  size_t right = n-1;
  size_t middle;
 
  while (left <= right)
    {
      middle = (left+right)/2;
      if (arr[middle] == elem)
    {
      return middle;
    }
      else if(arr[middle] < elem)
    {
      left = middle + 1;
    }
      else
    {
      right = middle - 1;
    }
      }
      return -1;
}


int main(int argc, char * argv[])
{
 
  int arr[] = {1,2,3,4,5,6,7} ;
  printf( "%d\n",  BinarySearch( arr, 7, 6 ) );
 
  return 0;
}

分治算法采用的是递归的思想,我们都知道递归的开销比较大,所以一般的分治算法也可以采用非递归来实现。下面以合并排序为例:
合并排序的递归算法:
#include
#include

void Merge( int *arr, size_t left, size_t right )
{
  size_t middle = (left + right)/2;
  size_t r = middle + 1;      
  size_t l = left;

  int *temp = calloc( right-left+1, sizeof(int) );
  int *p = temp;

  while (l<=middle && r<=right)
    {
      if (arr[l] <= arr[r] )
    {
      *p = arr[l];
      l++;
    }
      else
    {
      *p = arr[r];
      r++;
    }
      p++;
    }
 
  /* copy the rest elements in the left to the allocated array */
  if (l <= middle)          
    {
      *p = arr[l];
      p++;
      l++;
    }

  int *end = p;
  p = temp;
  l = left;

  /* copy it back to the array */
  while( p != end )
    {
      arr[l] = *p;
      p++;
      l++;
    }
free( temp );
}

 

void MergeSort( int *arr, size_t left, size_t right )
{
  if (left >= right)
    {
      return;
    }
  size_t k = (left + right)/2;
  MergeSort( arr, left, k );
  MergeSort( arr, k+1, right);
  Merge( arr, left, right );
}


int main(int argc, char * argv[])
{

  int arr[] = {1,5,7,2,6,3,4} ;
  MergeSort( arr, 0, 6 );

  size_t i = 0;
  for (i=0; i<7; i++)
    {
      printf( "%d ", arr[i] );
    }
  printf( "\n" );
 
  return 0;
   
}
递归算法是一种从上到下的思想,非递归则从下到上。递归将其分成大致相等的两组,那么到最后肯定是两个元素进行排序,我们可以从底向上,先两两排序,再四个一组进行排序,以此类推,这样与递归相比,少了合并的过程。下面给出全并排序的非递归算法:
#include
#include
#include
#include

/* tmp:  store the ordered elements for a while
   span: to indicate how many numbers we sort each time
 */
void MergeSpan( int *arr, size_t len, int *tmp, size_t i, size_t span )
{
  int end = i + span-1;
  end = (end>=(len-1))?(len-1):end; /* right limit */

  /* we will use i,span and tmp later, so don't change them */
  int fir = i;           
  int sec = i+span/2;        
  int *p = tmp;
 
  while (fir<=i+span/2-1 && sec<=end)
    {
      if (arr[fir] <= arr[sec])
    {
      *p = arr[fir];
      fir++;
    }
      else
    {
      *p = arr[sec];
      sec++;
    }
      p++;
    }
  while (fir<=i+span/2-1)
    {
      *p = arr[fir];
      fir++;
      p++;
    }

  /* copy it back to the array */
  int *ptr = tmp;
  fir = i;
  while (ptr < p)
    {
      arr[fir] = *ptr;
      ptr++;
      fir++;
    }
 
  //memset( tmp, 0, 2*span*sizeof(int) ); /* not necessary,  */
 
}

void MergeSort( int *arr, size_t len )
{
  int *tmp = calloc( len, sizeof(int) );
 
  size_t span = 2;
  size_t i = 0;
  size_t j = 1;
 
  while (span <= len)
    {
      span = (int)pow(2,j);
      j++;
      for (i=0; i    {
      MergeSpan( arr, len, tmp, i, span );
    }
    }
 
  //  free( tmp );
}


int main(int argc, char * argv[])
{
 
  int arr[] = {4,6,3,1,2,5};

  MergeSort( arr, 6);
   
  int i = 0;
  for (i=0; i<6; i++)
    {
      printf( "%d ", arr[i] );
    }
  printf( "\n" );

  return 0;
}
  
用gcc进行编译时需加上-lm选项。
上面的非递归算法中,我们是按一种平均的思想,即平均分成几组,事实上,有些元素可能已经排好序了,比如{5,1,2,3,4},如果我们将它们拆成两组{5}和{1,2,3,4},然后将这两组进行合并即可,快速排序算法其分成的两组也不是平均分配的。按照这种思想,我们可以编程实现如下:
#include
#include

#define NUM 7

/* 基本思想是将一个数组分成已经自然排序好的几个子数组段,再两两合并。由于每个数组段的长度不定,所以我们用链表来实现 ,比如{1,3,5,2,4,6}将*/

/* 将每个元素变成一个结点 */
typedef struct Node{
  size_t index;
  struct Node *next;
}Node;

/* 此链表存贮将每个子段的连起来 */
typedef struct NodeList
{
  Node *node;
  struct NodeList *next;
}NodeList;

/* 创建结点及子数段 */
void create_node( int *arr, size_t len, Node *node, NodeList *ndlt )
{
  node->index = 0;
  node->next = NULL;
  ndlt->node = node;
  ndlt->next = NULL;
  Node *nnext;
 
  size_t i = 0;

  for (i=1; i    {
      nnext = node+1;
      nnext->index = i;
      nnext->next = NULL;
          
      if (arr[nnext->index] >= arr[node->index])
    {
      node->next = nnext;
    }
      else            /* 当遇到非自然排序时创建新的链表 */
    {
      NodeList *nl = malloc(sizeof(NodeList)); /* 合并的过程中释放 */
      nl->node = nnext;
      nl->next = NULL;
      ndlt->next = nl;
      ndlt = nl;
      nl = NULL;
    }
      node = nnext;
                 
    }
}

/* 合并指定的两个链表 */
void mergenl( int *arr, NodeList *fir, NodeList *sec )
{
  Node *fn = fir->node;
  Node *sn = sec->node;
  Node *p = fir->node;

  if (arr[fn->index] >= arr[sn->index])
    {
      fir->node = sn;
      sn = sn->next;
    }
  else
    {
      fir->node = fn;
      fn = fn->next;
    }
  p = fir->node;
   
  while (fn != NULL && sn != NULL)
    {
      if (arr[fn->index] >= arr[sn->index] )
    {
      p->next = sn;
      sn = sn->next;
      p = p->next;
     
    }
      else
    {
      p->next = fn;
      fn = fn->next;
      p = p->next;
    }
    }

  while (fn != NULL)
    {
      p->next = fn;
      p=p->next;
      fn=fn->next;
    }
  while (sn != NULL)
    {
      p->next = sn;
      p = p->next;
      sn = sn->next;
    }
 
}

/* 进行两两合并 */
void merge( int *arr, NodeList *ndlt )
{
  NodeList *keep = ndlt;
  NodeList *merg;
 
  while (keep->next != NULL)
    {
      ndlt = keep;
      while ( ndlt->next != NULL )
    {
      merg = ndlt->next;
      mergenl( arr, ndlt, merg );
      if (merg->next != NULL)
        {
          ndlt->next = merg->next;
        }
      else
        {
          ndlt->next = NULL;
          break;
        }

      free( merg );       
    }
    }
}


int main(int argc, char * argv[])
{
  int arr[] = {7,4,6,2,1,5,3} ;
  Node node[NUM];
  NodeList ndlt;
 
  create_node( arr, NUM, node, &ndlt );
  merge( arr, &ndlt );
   
  Node *n = ndlt.node;
   
  while (n != NULL )
    {
      printf( "%d ", arr[n->index] );
      n=n->next;
    }

  printf( "\n" );
 
    
  return 0;
 
}


阅读(4045) | 评论(0) | 转发(0) |
0

上一篇:Unix域协议

下一篇:find命令

给主人留下些什么吧!~~