c++经典排序算法全集(转)
C++排序算法全集
排序算法是一种基本并且常用的算法。由于实际工作中处理的数量巨大,所以排序算法对算法本身的速度要求很高。 一、简单排序算法
由于程序比较简单,所以没有加什么注释。所有的程序都给出了完整的运行代码,并在我的VC环境下运行通过。因为没有涉及MFC和WINDOWS的内容,所以在BORLAND C++的平台上应该也不会有什么问题的。在代码的后面给出了运行过程示意,希望对理解有帮助。 .冒泡法:
这是最原始,也是众所周知的最慢的算法了。他的名字的由来因为它的工作看来象是冒泡:
#include <iostream.h> void BubbleSort(int* pData,int Count)
{
int iTemp;
for(int i=;i<Count;i++)
{
for(int j=Count-;j>=i;j--)
{
if(pData[j]<pData[j-])
{
iTemp = pData[j-];
pData[j-] = pData[j];
pData[j] = iTemp;
}
}
}
} void main()
{
int data[] = {,,,,,,};
BubbleSort(data,);
for (int i=;i<;i++)
cout<<data<<" ";
cout<<"\n";
} 倒序(最糟情况)
第一轮:,,,->,,,->,,,->,,,(交换3次)
第二轮:,,,->,,,->,,,(交换2次)
第一轮:,,,->,,,(交换1次)
循环次数:6次
交换次数:6次 其他:
第一轮:,,,->,,,->,,,->,,,(交换2次)
第二轮:,,,->,,,->,,,(交换0次)
第一轮:,,,->,,,(交换1次)
循环次数:6次
交换次数:3次 上面我们给出了程序段,现在我们分析它:这里,影响我们算法性能的主要部分是循环和交换,显然,次数越多,性能就越差。从上面的程序我们可以看出循环的次数是固定的,为1++...+n-。
写成公式就是1/*(n-)*n。
现在注意,我们给出O方法的定义: 若存在一常量K和起点n0,使当n>=n0时,有f(n)<=K*g(n),则f(n) = O(g(n))。(呵呵,不要说没学好数学呀,对于编程数学是非常重要的!!!) 现在我们来看1/*(n-)*n,当K=/,n0=,g(n)=n*n时,/*(n-)*n<=/*n*n=K*g(n)。所以f(n) =O(g(n))=O(n*n)。所以我们程序循环的复杂度为O(n*n)。
再看交换。从程序后面所跟的表可以看到,两种情况的循环相同,交换不同。其实交换本身同数据源的有序程度有极大的关系,当数据处于倒序的情况时,交换次数同循环一样(每次循环判断都会交换),复杂度为O(n*n)。当数据为正序,将不会有交换。复杂度为O()。乱序时处于中间状态。正是由于这样的原因,我们通常都是通过循环次数来对比算法。 .交换法:
交换法的程序最清晰简单,每次用当前的元素一一的同其后的元素比较并交换。
#include <iostream.h>
void ExchangeSort(int* pData,int Count)
{
int iTemp;
for(int i=;i<Count-;i++)
{
for(int j=i+;j<Count;j++)
{
if(pData[j]<pData)
{
iTemp = pData;
pData = pData[j];
pData[j] = iTemp;
}
}
}
} void main()
{
int data[] = {,,,,,,};
ExchangeSort(data,);
for (int i=;i<;i++)
cout<<data<<" ";
cout<<"\n";
}
倒序(最糟情况)
第一轮:,,,->,,,->,,,->,,,(交换3次)
第二轮:,,,->,,,->,,,(交换2次)
第一轮:,,,->,,,(交换1次)
循环次数:6次
交换次数:6次 其他:
第一轮:,,,->,,,->,,,->,,,(交换1次)
第二轮:,,,->,,,->,,,(交换1次)
第一轮:,,,->,,,(交换1次)
循环次数:6次
交换次数:3次 从运行的表格来看,交换几乎和冒泡一样糟。事实确实如此。循环次数和冒泡一样也是1/*(n-)*n,所以算法的复杂度仍然是O(n*n)。由于我们无法给出所有的情况,所以只能直接告诉大家他们在交换上面也是一样的糟糕(在某些情况下稍好,在某些情况下稍差)。 .选择法:
现在我们终于可以看到一点希望:选择法,这种方法提高了一点性能(某些情况下)
这种方法类似我们人为的排序习惯:从数据中选择最小的同第一个值交换,在从省下的部分中
选择最小的与第二个交换,这样往复下去。
#include <iostream.h>
void SelectSort(int* pData,int Count)
{
int iTemp;
int iPos;
for(int i=;i<Count-;i++)
{
iTemp = pData;
iPos = i;
for(int j=i+;j<Count;j++)
{
if(pData[j]<iTemp)
{
iTemp = pData[j];
iPos = j;
}
}
pData[iPos] = pData;
pData = iTemp;
}
} void main()
{
int data[] = {,,,,,,};
SelectSort(data,);
for (int i=;i<;i++)
cout<<data<<" ";
cout<<"\n";
}
倒序(最糟情况)
第一轮:,,,->(iTemp=),,,->(iTemp=),,,->(iTemp=),,,(交换1次)
第二轮:,,,->,,,(iTemp=)->(iTemp=),,,(交换1次)
第一轮:,,,->(iTemp=),,,(交换0次)
循环次数:6次
交换次数:2次 其他:
第一轮:,,,->(iTemp=),,,->(iTemp=),,,->(iTemp=),,,(交换1次)
第二轮:,,,->(iTemp=),,,->(iTemp=),,,(交换1次)
第一轮:,,,->(iTemp=),,,(交换1次)
循环次数:6次
交换次数:3次
遗憾的是算法需要的循环次数依然是1/*(n-)*n。所以算法复杂度为O(n*n)。
我们来看他的交换。由于每次外层循环只产生一次交换(只有一个最小值)。所以f(n)<=n
所以我们有f(n)=O(n)。所以,在数据较乱的时候,可以减少一定的交换次数。 .插入法:
插入法较为复杂,它的基本工作原理是抽出牌,在前面的牌中寻找相应的位置插入,然后继续下一张
#include <iostream.h>
void InsertSort(int* pData,int Count)
{
int iTemp;
int iPos;
for(int i=;i<Count;i++)
{
iTemp = pData;
iPos = i-;
while((iPos>=) && (iTemp<pData[iPos]))
{
pData[iPos+] = pData[iPos];
iPos--;
}
pData[iPos+] = iTemp;
}
} void main()
{
int data[] = {,,,,,,};
InsertSort(data,);
for (int i=;i<;i++)
cout<<data<<" ";
cout<<"\n";
} 倒序(最糟情况)
第一轮:,,,->,,,(交换1次)(循环1次)
第二轮:,,,->,,,(交换1次)(循环2次)
第一轮:,,,->,,,(交换1次)(循环3次)
循环次数:6次
交换次数:3次 其他:
第一轮:,,,->,,,(交换0次)(循环1次)
第二轮:,,,->,,,(交换1次)(循环2次)
第一轮:,,,->,,,(交换1次)(循环1次)
循环次数:4次
交换次数:2次 上面结尾的行为分析事实上造成了一种假象,让我们认为这种算法是简单算法中最好的,其实不是, 因为其循环次数虽然并不固定,我们仍可以使用O方法。从上面的结果可以看出,循环的次数f(n)<=/*n*(n-)<=/*n*n。所以其复杂度仍为O(n*n)(这里说明一下,其实如果不是为了展示这些简单排序的不同,交换次数仍然可以这样推导)。现在看交换,从外观上看,交换次数是O(n)(推导类似选择法),但我们每次要进行与内层循环相同次数的‘=’操作。正常的一次交换我们需要三次‘=’而这里显然多了一些,所以我们浪费了时间。 最终,我个人认为,在简单排序算法中,选择法是最好的。 二、高级排序算法:
高级排序算法中我们将只介绍这一种,同时也是目前我所知道(我看过的资料中)的最快的。
它的工作看起来仍然象一个二叉树。首先我们选择一个中间值middle程序中我们使用数组中间值,然后把比它小的放在左边,大的放在右边(具体的实现是从两边找,找到一对后交换)。然后对两边分别使用这个过程(最容易的方法——递归)。 .快速排序:
#include <iostream.h> void run(int* pData,int left,int right)
{
int i,j;
int middle,iTemp;
i = left;
j = right;
middle = pData[(left+right)/]; //求中间值
do{
while((pData<middle) && (i<right))//从左扫描大于中值的数
i++;
while((pData[j]>middle) && (j>left))//从右扫描大于中值的数
j--;
if(i<=j)//找到了一对值
{
//交换
iTemp = pData;
pData = pData[j];
pData[j] = iTemp;
i++;
j--;
}
}while(i<=j);//如果两边扫描的下标交错,就停止(完成一次) //当左边部分有值(left<j),递归左半边
if(left<j)
run(pData,left,j);
//当右边部分有值(right>i),递归右半边
if(right>i)
run(pData,i,right);
} void QuickSort(int* pData,int Count)
{
run(pData,,Count-);
} void main()
{
int data[] = {,,,,,,};
QuickSort(data,);
for (int i=;i<;i++)
cout<<data<<" ";
cout<<"\n";
} 这里我没有给出行为的分析,因为这个很简单,我们直接来分析算法:首先我们考虑最理想的情况
.数组的大小是2的幂,这样分下去始终可以被2整除。假设为2的k次方,即k=log2(n)。
.每次我们选择的值刚好是中间值,这样,数组才可以被等分。
第一层递归,循环n次,第二层循环2*(n/)......
所以共有n+(n/)+(n/)+...+n*(n/n) = n+n+n+...+n=k*n=log2(n)*n
所以算法复杂度为O(log2(n)*n)
其他的情况只会比这种情况差,最差的情况是每次选择到的middle都是最小值或最大值,那么他将变成交换法(由于使用了递归,情况更糟)。但是你认为这种情况发生的几率有多大??呵呵,你完全不必担心这个问题。实践证明,大多数的情况,快速排序总是最好的。
如果你担心这个问题,你可以使用堆排序,这是一种稳定的O(log2(n)*n)算法,但是通常情况下速度要慢于快速排序(因为要重组堆)。 三、其他排序
.双向冒泡:
通常的冒泡是单向的,而这里是双向的,也就是说还要进行反向的工作。
代码看起来复杂,仔细理一下就明白了,是一个来回震荡的方式。
写这段代码的作者认为这样可以在冒泡的基础上减少一些交换(我不这么认为,也许我错了)。
反正我认为这是一段有趣的代码,值得一看。
#include <iostream.h>
void Bubble2Sort(int* pData,int Count)
{
int iTemp;
int left = ;
int right =Count -;
int t;
do
{
//正向的部分
for(int i=right;i>=left;i--)
{
if(pData<pData[i-])
{
iTemp = pData;
pData = pData[i-];
pData[i-] = iTemp;
t = i;
}
}
left = t+; //反向的部分
for(i=left;i<right+;i++)
{
if(pData<pData[i-])
{
iTemp = pData;
pData = pData[i-];
pData[i-] = iTemp;
t = i;
}
}
right = t-;
}while(left<=right);
} void main()
{
int data[] = {,,,,,,};
Bubble2Sort(data,);
for (int i=;i<;i++)
cout<<data<<" ";
cout<<"\n";
} .SHELL排序
这个排序非常复杂,看了程序就知道了。
首先需要一个递减的步长,这里我们使用的是9、、、(最后的步长必须是1)。
工作原理是首先对相隔9-1个元素的所有内容排序,然后再使用同样的方法对相隔5-1个元素的排序以次类推。
#include <iostream.h>
void ShellSort(int* pData,int Count)
{
int step[];
step[] = ;
step[] = ;
step[] = ;
step[] = ; int iTemp;
int k,s,w;
for(int i=;i<;i++)
{
k = step;
s = -k;
for(int j=k;j<Count;j++)
{
iTemp = pData[j];
w = j-k;//求上step个元素的下标
if(s ==)
{
s = -k;
s++;
pData[s] = iTemp;
}
while((iTemp<pData[w]) && (w>=) && (w<=Count))
{
pData[w+k] = pData[w];
w = w-k;
}
pData[w+k] = iTemp;
}
}
} void main()
{
int data[] = {,,,,,,,,,,-,-};
ShellSort(data,);
for (int i=;i<;i++)
cout<<data<<" ";
cout<<"\n";
}
呵呵,程序看起来有些头疼。不过也不是很难,把s==0的块去掉就轻松多了,这里是避免使用0步长造成程序异常而写的代码。这个代码我认为很值得一看。 这个算法的得名是因为其发明者的名字D.L.SHELL。依照参考资料上的说法:“由于复杂的数学原因避免使用2的幂次步长,它能降低算法效率。”另外算法的复杂度为n的1.2次幂。同样因为非常复杂并“超出本书讨论范围”的原因(我也不知道过程),我们只有结果了。 四、基于模板的通用排序:
这个程序我想就没有分析的必要了,大家看一下就可以了。不明白可以在论坛上问。
MyData.h文件
///////////////////////////////////////////////////////
class CMyData
{
public:
CMyData(int Index,char* strData);
CMyData();
virtual ~CMyData(); int m_iIndex;
int GetDataSize(){ return m_iDataSize; };
const char* GetData(){ return m_strDatamember; };
//这里重载了操作符:
CMyData& operator =(CMyData &SrcData);
bool operator <(CMyData& data );
bool operator >(CMyData& data ); private:
char* m_strDatamember;
int m_iDataSize;
};
//////////////////////////////////////////////////////// MyData.cpp文件
////////////////////////////////////////////////////////
CMyData::CMyData():
m_iIndex(),
m_iDataSize(),
m_strDatamember(NULL)
{
} CMyData::~CMyData()
{
if(m_strDatamember != NULL)
delete[] m_strDatamember;
m_strDatamember = NULL;
} CMyData::CMyData(int Index,char* strData):
m_iIndex(Index),
m_iDataSize(),
m_strDatamember(NULL)
{
m_iDataSize = strlen(strData);
m_strDatamember = new char[m_iDataSize+];
strcpy(m_strDatamember,strData);
} CMyData& CMyData::operator =(CMyData &SrcData)
{
m_iIndex = SrcData.m_iIndex;
m_iDataSize = SrcData.GetDataSize();
m_strDatamember = new char[m_iDataSize+];
strcpy(m_strDatamember,SrcData.GetData());
return *this;
} bool CMyData::operator <(CMyData& data )
{
return m_iIndex<data.m_iIndex;
} bool CMyData::operator >(CMyData& data )
{
return m_iIndex>data.m_iIndex;
}
/////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
//主程序部分
#include <iostream.h>
#include "MyData.h" template <class T>
void run(T* pData,int left,int right)
{
int i,j;
T middle,iTemp;
i = left;
j = right;
//下面的比较都调用我们重载的操作符函数
middle = pData[(left+right)/]; //求中间值
do{
while((pData<middle) && (i<right))//从左扫描大于中值的数
i++;
while((pData[j]>middle) && (j>left))//从右扫描大于中值的数
j--;
if(i<=j)//找到了一对值
{
//交换
iTemp = pData;
pData = pData[j];
pData[j] = iTemp;
i++;
j--;
}
}while(i<=j);//如果两边扫描的下标交错,就停止(完成一次) //当左边部分有值(left<j),递归左半边
if(left<j)
run(pData,left,j);
//当右边部分有值(right>i),递归右半边
if(right>i)
run(pData,i,right);
} template <class T>
void QuickSort(T* pData,int Count)
{
run(pData,,Count-);
} void main()
{
CMyData data[] = {
CMyData(,"xulion"),
CMyData(,"sanzoo"),
CMyData(,"wangjun"),
CMyData(,"VCKBASE"),
CMyData(,"jacky2000"),
CMyData(,"cwally"),
CMyData(,"VCUSER"),
CMyData(,"isdong")
};
QuickSort(data,);
for (int i=;i<;i++)
cout<<data.m_iIndex<<" "<<data.GetData()<<"\n";
cout<<"\n"; ////////////////////////////////////////////////////////
经典C++双向冒泡排序算法
经典C++双向冒泡排序算法
hawkman2k 发表于 --
#include《iostream.h》
#define max 20 //最多记录个数
typedef int elemtype;
typedef elemtype recs[max];
void bibubble(recs r,int n)
{
int flag=; //继续遍历时flag置1,已排好序不需遍历时为0
int i=, j;
elemtype temp;
while(flag==)
{
flag=;
for(j=i+;j《n-;j++) //正向遍历找最大值
if(r[j]》r[j+])
{
flag=; //能交换时,说明未排好序,需继续
temp=r[j];
r[j]=r[j+];
r[j+]=temp;
}
for(j=n-i-;j》=i+;j--) //反向遍历
if(r[j]》r[j-])
{
flag=; //能交换时,说明未排好序,需继续
temp=r[j];
r[j]=r[j-];
r[j-]=temp;
}
i++;
}
} void main()
{
recs A={,,,,,,,,,};
int n=, i;
cout《《"双向冒泡排序"《《endl《《"排序前:";
for(i=;i《n;i++)
cout《《A[i]《《"";
cout《《endl;
cout《《" 排序后: ";
bibubble(A,n);
for(i=;i《n;i++)
cout《《A[i]《《"";
cout《《endl;
}
转:http://blog.csdn.net/yangxiao_xiang
c++经典排序算法全集(转)的更多相关文章
- 经典排序算法 – 插入排序Insertion sort
经典排序算法 – 插入排序Insertion sort 插入排序就是每一步都将一个待排数据按其大小插入到已经排序的数据中的适当位置,直到全部插入完毕. 插入排序方法分直接插入排序和折半插入排序两种, ...
- 经典排序算法总结与实现 ---python
原文:http://wuchong.me/blog/2014/02/09/algorithm-sort-summary/ 经典排序算法在面试中占有很大的比重,也是基础,为了未雨绸缪,在寒假里整理并用P ...
- 经典排序算法及python实现
今天我们来谈谈几种经典排序算法,然后用python来实现,最后通过数据来比较几个算法时间 选择排序 选择排序(Selection sort)是一种简单直观的排序算法.它的工作原理是每一次从待排序的数据 ...
- 经典排序算法 - 基数排序Radix sort
经典排序算法 - 基数排序Radix sort 原理类似桶排序,这里总是须要10个桶,多次使用 首先以个位数的值进行装桶,即个位数为1则放入1号桶,为9则放入9号桶,临时忽视十位数 比如 待排序数组[ ...
- 经典排序算法 - 高速排序Quick sort
经典排序算法 - 高速排序Quick sort 原理,通过一趟扫描将要排序的数据切割成独立的两部分,当中一部分的全部数据都比另外一部分的全部数据都要小,然后再按此方法对这两部分数据分别进行高速排序,整 ...
- 经典排序算法 - 归并排序Merge sort
经典排序算法 - 归并排序Merge sort 原理,把原始数组分成若干子数组,对每个子数组进行排序, 继续把子数组与子数组合并,合并后仍然有序,直到所有合并完,形成有序的数组 举例 无序数组[6 2 ...
- C# 经典排序算法大全
C# 经典排序算法大全 选择排序 using System; using System.Collections.Generic; using System.Linq; using System.Tex ...
- Jerry 2017年的五一小长假:8种经典排序算法的ABAP实现
2017年4月29日~5月1日,国际劳动节, 三天的小长假. 在国内,小长假往往是这样的: 然而我当时在戏称为"德村"(德国农村)的Walldorf出差并且住在Wiesloch, ...
- 【最全】经典排序算法(C语言)
算法复杂度比较: 算法分类 一.直接插入排序 一个插入排序是另一种简单排序,它的思路是:每次从未排好的序列中选出第一个元素插入到已排好的序列中. 它的算法步骤可以大致归纳如下: 从未排好的序列中拿出首 ...
随机推荐
- UVA-10816 Travel in Desert (最小瓶颈最短路)
题目大意:给一张无向图,图中的每条边都有两个权值,长度d和热度r.找出从起点到终点的一条最大热度最小的路径,如果这样的路径有多条,选择一个最短的. 题目分析:如果只考虑最小的最大热度,那么本题就是一个 ...
- SQL向一个表中批量插入&&删除大量数据
插入: 1. 数据从另一个表中获取 (1)两表结构不一样insert into tb1 需要的列名 select 按照前面写上需要的列名 from tb2(2)两表结构一样insert into tb ...
- 改变Vim保存文件路径
1. vim 有个cd命令.用来更改当前文件夹.:cd sth进入sth文件夹.这样新文件保存之后就在当前文件夹.不过如果你打开一个已经保存的文件后然后更改当前文件夹是不会改变保存路径的.你必须为:w ...
- hdu 3682 10 杭州 现场 C - To Be an Dream Architect 简单容斥 难度:1
C - To Be an Dream Architect Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d &a ...
- linux下informatica服务安装和配置
本文中将会用infa简称代替informatica 1.安装前准备 介质名称 版本信息 描述 Informatica Powercenter 9.5.1 for Linux 64 bit 必须 Jav ...
- [置顶]
Android Studio apk打包以及获取apk签名信息
首先说下Android Studio 主要分为以下几步 填写你的签名的一些信息 (例如签名的文件名.省份.密码 别名 就是你比如叫xxx 但是别名是xx张三 认证年限就是apk过期默认是25年 其他就 ...
- Codeforces 1009D:Relatively Prime Graph
D. Relatively Prime Graph time limit per test 2 seconds memory limit per test 256 megabytes input st ...
- GIST特征描述符使用(转)
GIST特征描述符使用 一种场景特征描述 场景特征描述? 通常的特征描述符都是对图片的局部特征进行描述的,以这种思路进行场景描述是不可行的. 比如:对于“大街上有一些行人”这个场景,我们必须通过局部特 ...
- test20181020 B君的第一题
题意 分析 二次剩余问题. x,y相当于二次方程 \[ x^2-bx+c=0 \mod{p} \] 的两根. 摸意义下的二次方程仍然考虑判别式\(\Delta=b^2-4c\). 它能开根的条件是\( ...
- Python threadpool传递参数
threadpool模块是一个很老的实现python线程池的模块,pypi已经建议用multiprocessing代替它了,但是,它使用的便捷性还是征服了一批忠实用户. threadpool模块实现多 ...