排序-算法总结

==============================
冒泡排序算法
==============================
1.算法稳定性
   稳定

2.复杂度
   空间复杂度O(1);  平均时间复杂度O(n2)

3.极限情况分析
   最好情况:向量本来就是有序的,则一趟扫描即可结束,共比较n-1次,无交换。
   最坏情况:向量是逆序的,则一共需要做n-1次扫描,每次扫描都必须比较n-i次,由等差数列求和公式知道,一共需做n(n-1)/2次比较和交换。

4.算法实现

<span style="font-size:13px;"><span style="font-size:13px;"></span></span><pre class="cpp" name="code">template<typename T>
void bubble_sort(T * array, const int size)
{
 if(size < 2 || array == NULL)
 {
  cout << "illegal input!" << endl;
  return;
 }
 
 bool exchanged = false;
  T tmp;

 for(int i=0; i<size-1; ++i)
 {
  exchanged = false;

  for(int j=size-1; j>i; --j)
  {
   if(array[j] < array[j-1])
   {
    tmp = array[j];
    array[j] = array[j-1];
    array[j-1] = tmp;
    exchanged = true;
   }
  }
 
  if(!exchanged)
  {
   return;
  }
 }
}
</pre>
<pre></pre>
<p><span style="font-size:13px">5. 算法改进<br>
(1) 奇偶交换排序<br>
     稳定性? 稳定 <span style="color:#ff0000">//TODO need verifying<br>
</span>     空间复杂度O(1), 时间复杂度O(n2)<br>
     极限情况分析:正向有序:n-1次比较;逆向有序((n+1)/2)*n+(n/2-1)次比较<br>
     综合而言,性能和冒泡排序相当,所以一般不用此种排序方法。<br>
     算法实现:</span></p>
<pre class="cpp" name="code"><span style="font-size:13px;">template<typename T>
void oe_sort(T * array,  const int size)
{
 if(size < 2 || array == NULL)
 {
  cout << "illegal input!" << endl;
  return;
 }

 T tmp;
 bool exchanged = true;
 while(exchanged)
 {
  exchanged = false;

  //scan odd index
  for(int i=1; i<size-1; i+=2)
  {
   if(array[i] > array[i+1])
   {
    tmp = array[i];
    array[i] = array[i+1];
    array[i+1] = tmp;
    exchanged = true;
   }
  }

  //scan even index
  for(int j=0; j<size-1; j+=2)
  {
   if(array[j] > array[j+1])
   {
    tmp = array[j];
    array[j] = array[j+1];
    array[j+1] = tmp;
    exchanged = true;
   }
  }
 }
}
</span></pre>
<p><br>
<br>
<span style="font-size:13px">  <br>
(2) 每次扫描记住最后一次扫描发生的位置lastExchange<br>
     如此一样,下一趟排序时区间[0, lastExchange-1]是有序区, [lastExchange, size-1]是无序区。如果不记录,则下一趟排序时区间[0, i]有序区间,i为上次排序的趟数。自然的,lastExchange的最小可能数值就是i,所以此种改性是有效的。<br>
     算法性能和普通冒泡一样。<br>
     算法实现: </span><span style="font-size:13px"><span style="color:#ff0000">//TODO<br>
</span> <br>
(3) 改进冒泡算法的不对称性<br>
     什么是不对称性? 比如上面的冒泡排序算法,如果数组中的最大值位于a[0],理论上一遍扫描就可以完成排序,但实际仍旧需要n-1遍扫描。而如果改冒泡为沉底,则一遍即可。<br>
     所以引出了解决不对称性的方法:冒泡和沉底交替进行。<br>
     算法性能和普通冒泡一样。<br>
     算法实现: </span><span style="font-size:13px"><span style="color:#ff0000">//TODO<br>
</span> <br>
 <br>
==========================<br>
快速排序算法<br>
==========================<br>
1. 基本思想<br>
   分治法:将原问题分解为若干个规模更小但结构与原问题相似的子问题。递归地解这些子问题,然后将这些子问题的解组合为原问题的解。</span></p>
<p><span style="font-size:13px">2. 算法描述<br>
    核心是:选取哨兵结点,划分区域,并找到哨兵结点所在的位置。<br>
    (1) 选取一个哨兵结点,通常取首结点,或者末结点,或者中间结点。但最合理的应该是随机选择一个结点。<br>
    (2) 根据哨兵结点,将原数组划分为左右2个部分,并决定哨兵结点的位置。<br>
    (3) 分别对左右部分递归地进行(1)和(2),递归结束条件是分出的子数组长度小于等于1</span></p>
<p><span style="font-size:13px">3. 算法稳定性<br>
   不稳定</span></p>
<p><span style="font-size:13px">4. 复杂度<br>
   平均空间复杂度是O(lgn), 注意是以2为底的对数。<br>
   平均时间复杂度是O(nlgn)。</span></p>
<p><span style="font-size:13px">5. 极限情况分析<br>
   最好情况下,空间复杂度是O(lgn), 时间复杂度是O(nlgn)。此处的空间复杂度主要是递归产生的栈空间。<br>
   最坏情况下,空间复杂度是O(n), 时间复杂度是O{n2}。最坏情况是每次哨兵结点都是整个数组的最大值,或者最小值。<br>
  <br>
6. 算法实现<br>
</span></p>
<pre class="cpp" name="code">template<typename T>
int partition(T * array, const int low, const int high)
{
 //select the first elem as pivot. Here use random index for pivot will be better
 T pivot = array[low];
 for(int i=low, j=high; i<j;)
 {
  //scan for the first elem that smaller than pivot
  while(j>i && array[j]>=pivot)
   j--;
  if(i<j)
  {
   //since array[i] is already be array[j] which is smaller than pivot, array[i+1] will firstly checkde in next scan
   array[i++] = array[j];
  }

  //scan for the first elem that bigger than pivot
  while(i<j && array[i]<=pivot)
  i++;
  if(i<j)
  {
   array[j--] = array[i];
  }
 }
 
 //put the pivot to right position, pls notice that i will be equal with j at this point
 array[i] = pivot;
    
 return i;
}

template<typename T>
quick_sort(T * array, const int low, const int high)
{
 int pivotPos = 0;

 if(low < high)
 {
  //the most important is locating index for pivot, then partition will be possible
  pivotPos = partition(array, low, high);
  quick_sort(array, low, pivotPos-1);
  quick_sort(array, pivotPos+1, high);
 }
}
</pre>
<p><span style="font-size:13px">注意:冒泡排序和快速排序均属于交换排序算法。</span></p>
<p><span style="font-size:13px"> </span></p>
<p><span style="font-size:13px">========================<br>
直接插入排序算法<br>
========================<br>
1.算法稳定性<br>
   稳定<br>
 <br>
2.复杂度<br>
  空间复杂度O(1); 时间复杂度O(n2)<br>
  <br>
3.极限情况分析<br>
  最好情况:向量是正序的,共比较关键字n-1次,无交换。<br>
  最坏情况:向量是逆序的,则一共比较关键字│(n+2)(n-1)/2次,记录移动次数n-1)(n+4)/2次<br>
  <br>
4. 算法实现<br>
</span></p>
<pre class="cpp" name="code">template<typename T>
void insert_sort(T * array, const int size)
{
 if(size < 2 || array == NULL)
 {
  cout << "illegal input!" << endl;
  return;
 }

 T pivot;

 for(int i=1; i<size; ++i)
 {
  if(array[i] >= array[i-1])
  {
   continue;
  }

  pivot = array[i];
  int j = i-1;
  do
  {
   array[j+1] = array[j];
   --j;
  }while(pivot < array[j] && j>=0);
 
  array[j+1] = pivot;
 }
}
</pre>
<p> </p>
<p><span style="font-size:13px">========================<br>
希尔排序算法<br>
========================<br>
1.算法稳定性<br>
  不稳定<br>
  <br>
2.算法描述<br>
  (1)直接插入排序的变体,取一个小于数组长度n的整数d作为初始增量,将整个数组分为d个小组。<br>
  比如说整个数组的长度为7,选择初始增量d为5, 则a[0], a[5]为第一组。a[1]和a[6]为第二组,a[2]和a[7]为第三组,a[3]为第四组,a[4]为第五组<br>
  然后在每个小组内进行直接插入排序。<br>
  (2)按照一定的减少d, 比如说第二次改变为3,再次进行上述的(1)和(2)。直到d减少到为1,并最后一次执行直接排序为止。<br>
 <br>
3.复杂度<br>
  空间复杂度O(1); 时间复杂度O(n(lgn)2) 译为n乘以lgn的平方。其复杂度收到增量递减方式的影响。最好的步长序列由Marcin Ciura设计为{1, 4, 10, 23, 57, 132, 301, 701, 1750, ...}<br>
  <br>
4.极限情况分析<br>
  最好情况:向量是正序的,共比较关键字n-1次,无交换。<br>
  最坏情况:向量是逆序的,则一共比较关键字│(n+2)(n-1)/2次,记录移动次数n-1)(n+4)/2次<br>
  <br>
5.算法实现</span></p>
<pre class="cpp" name="code">template<typename T>
void shellInteration(T * array, const int size, const int increment)
{
 T pivot;

 for(int i=increment; i<size; ++i)  //此算法中,将所有的increment改成1,就退化成了直接插入排序算法
 {
  if(array[i] >= array[i-increment])
  {
   continue;
  }
 
  pivot = array[i];
  int j = i-increment;
  do
  {
   array[j+increment] = array[j];
   j -= increment;
  }while(pivot < array[j] && j>=0);
 
  array[j+increment] = pivot;
 }
}

template<typename T>
void shellSort(T * array, const int size)
{
 if(size < 2 || array == NULL)
 {
  cout << "illegal input!" << endl;
  return;
 }
 
 int increaArray[3] = {5, 3, 1}; //增量序列,必须以1结束

 for(int i=0; i<sizeof(increaArray)/sizeof(int); ++i)
 {
  shellInteration(array, size, increaArray[i]);
 }
}
</pre>
<p> </p>
<p><span style="font-size:13px">注意:直接插入排序和希尔排序都是插入排序。</span></p>
<p><span style="font-size:13px"> </span></p>
<p><span style="font-size:13px">========================<br>
直接选择排序算法<br>
========================<br>
1. 基本思想<br>
   进行n-1次扫描,每次从无序区中选择关键字最小的记录,并移动到有序区中。</span></p>
<p><span style="font-size:13px">2. 算法稳定性<br>
   不稳定</span></p>
<p><span style="font-size:13px">3. 复杂度<br>
   平均空间复杂度是O(l)。<br>
   平均时间复杂度是O(n2)。</span></p>
<p><span style="font-size:13px">4. 极限情况分析<br>
   最好情况下,空间复杂度是O(1), 时间复杂度是O(n2)。此处的空间复杂度,即便在正向有序的情况下,第i趟都需要比较关键字n-i次。<br>
   最坏情况下,空间复杂度是O(1), 时间复杂度是O{n2}。</span></p>
<p><span style="font-size:13px">5. 算法实现</span></p>
<pre class="cpp" name="code">template<typename T>
void select_sort(T * array, const int size)
{
 if(size < 2 || array == NULL)
 {
  cout << "illegal input!" << endl;
  return;
 }

 //pivotIndex index to monitor the smallest elem in every scan
 int pivotIndex = 0;

 for(int i=0; i<size; ++i)
 {
  pivotIndex = i;
  for(int j=i+1; j<size; ++j)
  {
   if(array[j] < array[pivotIndex])
    pivotIndex=j;
  }

  if(pivotIndex!=i)
  {
   T tmp = array[pivotIndex];
   array[pivotIndex] = array[i];
   array[i] = tmp;
  }
 }
}
</pre>
<p><br>
<span style="font-size:13px">========================<br>
堆排序算法<br>
========================<br>
1. 基本思想<br>
   在排序过程中,将R[l..n]看成是一棵完全二叉树的顺序存储结构,利用完全二叉树中双亲结点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(或最小)的记录。</span></p>
<p><span style="font-size:13px">2. 算法稳定性<br>
   不稳定</span></p>
<p><span style="font-size:13px">3. 复杂度<br>
   平均空间复杂度是O(l)。<br>
   平均时间复杂度是O(nlogn)。</span></p>
<p><span style="font-size:13px">4. 极限情况分析<br>
   最坏情况下,时间复杂度是O(nlogn)。</span></p>
<p><span style="font-size:13px">5. 算法实现</span></p>
<pre class="cpp" name="code"><span style="font-size:13px;">template<typename T>
void heapAdjust(T * array, const int startIndex, const int length)
{
 //注意此函数的功能不是建立大根堆,只是做有限的调整
 
 int pivotIndex = startIndex;

 while(2*pivotIndex+1 < length)
 {
  int lChildIndex = 2*pivotIndex+1, rChildIndex = 2*pivotIndex+2;
 
  //取得左右孩子中较大值的下标
  int maxChildIndex = lChildIndex;
  if(rChildIndex < length)
  {
   maxChildIndex = array[lChildIndex]<array[rChildIndex]?rChildIndex:lChildIndex;
  }

  //如果孩子结点的值大于父亲结点的值,则交换2个结点值
  if(array[maxChildIndex] > array[pivotIndex])
  {
   T tmp = array[pivotIndex];
   array[pivotIndex] = array[maxChildIndex];
   array[maxChildIndex] = tmp;

   //交换后,子堆被破坏,继续调整子堆
   pivotIndex = maxChildIndex;
  }
  else
  {
   break; //如果已经符合了大根堆的规则,则直接退出
  }
 }
}

template<typename T>
void heap_sort(T * array, const int size)
{
 if(size < 2 || array == NULL)
 {
  cout << "illegal input!" << endl;
  return;
 }
 
  //第一步,把数组看成是完全二叉树的层次遍历存储,并将整个数组建成一个大根二叉堆
  //size/2-1所标记的结点,完全二叉树的最后一个叶子结点的父结点
 for(int i=size/2-1; i>=0; --i)
 {
  heapAdjust(array, i, size);
 
  //test
  //printResult(cout, array, 10);
 }

 //经过第一步后,array[0]存储的是此数组中最大值,将array[0]和array[size-1]交换,这样无序区就变成了
 //array[0, size-2],再对无序区进行大根堆的调整。
 //这里需要注意一点:array[0, size-2]成为不符合大根堆原则的区间,仅仅因为调整后的根结点array[0]位置不对,
 //所以只需要将array[0]“下沉交换”到合适的层次,使得整个区间再次成为大根堆时,array[0]变再次成为子区间中的最大值。依次类推。
 for(int j=size-1; j>0; --j)
 {
  T tmp = array[0];
  array[0] = array[j];
  array[j] = tmp;
  heapAdjust(array, 0, j); //TODO: I suspect that it should be j-1 here
 }
}
</span></pre>
<p> </p>
<p><span style="font-size:13px">注意:直接选择排序和堆排序都属于选择排序的范畴。</span></p>
<p><br>
<span style="font-size:13px">========================<br>
箱排序算法<br>
========================<br>
1. 基本思想<br>
   箱排序也称桶排序(Bucket Sort),其基本思想是:设置若干个箱子,依次扫描待排序的记录R[0],R[1],…,R[n-1],把关键字等于k的记录全都装入到第k个箱子里(分配),然后按序号依次将各非空的箱子首尾连接起来(收集)。</span></p>
<p><span style="font-size:13px">2. 算法稳定性<br>
   如果保证元素入箱和出箱时都遵守FIFO,则箱排序是稳定的。<br>
  <br>
3. 复杂度<br>
    平均空间复杂度是O(n)。<br>
    平均时间复杂度是O(m+n)。其中,m是箱子的个数。如果箱子个数的数量级是O{n},则时间复杂度是O{n}<br>
 <br>
4. 极限情况分析<br>
   最坏情况下,时间复杂度是o{n2}。比如说一个有10个元素的数组排序,元素的取值范围是1-100, 用100个箱子进行排序,此时m=n2=100, 时间复杂度也变成了O(n2).</span></p>
<p><span style="font-size:13px">5. 算法实现<br>
<span style="color:#ff0000">//TODO</span></span></p>
<p><span style="font-size:13px; color:#ff0000"></span> </p>
<p><span style="font-size:13px">========================<br>
基数排序算法<br>
========================<br>
1. 基本思想<br>
   定义比较抽象,以十进制的整数数组为例说明。一个十进制整数的每一位的取值范围是0-9,可能的取值个数是10,这个可能的取值个数成为基数。<br>
再比如是英文小写字符数组,则取值范围是'a'到'z',一共26个可能的取值,基数则为26。<br>
  首先设置基数个箱子,比如整数数组排序,则设置10个箱子,序号从0到9。然后从低位到高位对每个元素进行箱排序。排序所需要进行的趟数,是最大的元素位数。比如说一个整数数组,最大元素是1000,其它元素都小于1000,则需要进行4趟扫描。</span></p>
<p><span style="font-size:13px">2. 算法稳定性<br>
   要保证基数排序的正确性,必须保证除开第一趟外的每趟箱排序是稳定的。这点保证了基数排序本身也是稳定的。<br>
  <br>
3. 复杂度<br>
   平均时间复杂度 O{n}<br>
   平均空间复杂度 O(n+基数)</span></p>
<p><span style="font-size:13px">4. 算法实现<br>
<span style="color:#ff0000">  //TODO</span><br>
 </span></p>
<p><span style="font-size:13px"></span><span style="font-size:13px"><br>
========================<br>
归并排序算法<br>
========================<br>
1. 基本思想<br>
   算法导论上第一章中谈分治法时的例子,基于二路归并操作(对2个有序数组进行合并,形成新的有序序列)。首先将整个数组不断二分直至只有一个元素(已然有序),然后对子数组进行二路归并操作,最后合并操作结果。</span></p>
<p><span style="font-size:13px">2. 算法的稳定性<br>
   是稳定的。</span></p>
<p><span style="font-size:13px">3. 复杂度<br>
   平均时间复杂度 O{nlgn}, 空间复杂度O{n}。就时间复杂度为言,和快速排序一样,但快速排序是就地排序。<br>
  <br>
4. 算法实现</span></p>
<pre class="cpp" name="code">//merge [left, middle] and [middle+1, right], then copy back to array
template<typename T>
void mergeAndCopy(T * array, const int left, int middle, int right)
{
 T * buff = new T[right-left+1];
 
 //merge two part of array
    int i=left, j=middle+1;
 int buff_index=0;
 while(i<=middle && j<=right)
 {
  buff[buff_index++] = array[i]<=array[j]?array[i++]:array[j++];
 }

 //merge the rest part
 while(i<=middle)
 {
  buff[buff_index++] = array[i++];
 }
 while(j<=right)
 {
  buff[buff_index++] = array[j++];
 }
 
 //copy buff data back to original array
 for(int k=left, index=0; k<=right; ++k, ++index)
 {
  array[k] = buff[index];
 }
}

template<typename T>
void mergeSort(T * array, const int left, const int right)
{
 if(NULL == array)
 {
  cout << "illegal input" << endl;
  return;
 }

 if(left < right)
 {
  int middle = (left + right)/2;
  mergeSort(array, left, middle);
  mergeSort(array, middle+1, right);
  mergeAndCopy(array,left,middle,right);
 }
}
</pre>
   


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值