十种排序算法的C++泛型实现

关于排序,已经有大量的文献可以参阅了,如果你看着大摞大摞的书和论文觉得头大,那么网上有一篇极好的文章,在这里这里,和这里,作者Matrix67牛。不过大牛代码之间游刃有余,基本上没有任何代码实现,对于初学者们来说,代码还是很重要的,不管是读代码还是写代码。学生时代写代码的机会本来就很萧条,有写代码的时候千万不要得过且过。下面是一个简单的模板实现,但高手们不要见——高手都不用来看这篇水文——俺是个懒人呀,没有实现算法真正的泛化,STL那么精密巧妙的实现,俺这种菜鸟学不来。不过,如果有下一回,我会对STL中的算法的实现做一简单的分析,看看那些巨牛B的代码到底是怎么实现的,并对性能做一测试。

 

1

  2

  3

  4

  5

  6

  7

  8

  9

 10

 11

 12

 13

 14

 15

 16

 17

 18

 19

 20

 21

 22

 23

 24

 25

 26

 27

 28

 29

 30

 31

 32

 33

 34

 35

 36

 37

 38

 39

 40

 41

 42

 43

 44

 45

 46

 47

 48

 49

 50

 51

 52

 53

 54

 55

 56

 57

 58

 59

 60

 61

 62

 63

 64

 65

 66

 67

 68

 69

 70

 71

 72

 73

 74

 75

 76

 77

 78

 79

 80

 81

 82

 83

 84

 85

 86

 87

 88

 89

 90

 91

 92

 93

 94

 95

 96

 97

 98

 99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

//争取把排序算法都总结出来,因为排序既是基础,又内容较多,

//当然最重要的,也还算有趣。

//其次就是总结这一思维过程的乐趣了。

 

namespace _SORT_HELP{

    template<typename T>

    inline void swap(T& _x,T& _y)

    {

        T temp=x;

        x=y;

        y=temp;

    }

}

 

 

//以下均默认为从小到大排序

 

#include<iostream>

#include<limits>

 

using _SORT_HELP::swap;

 

/******************************************************************************************************************

选择排序,每次从数列中选出最小(大)的数字放到最前面说实话这算法实在是笨拙,没啥用,不过生活中倒是有对应当人们渴望先知道排在前面的是谁时,就是选择排序的思路复杂度是稳稳当当的 n^2

******************************************************************************************************************/

template<typename T>void selection_sort(T a[],int n)

{

    for(int i=0;i<n;i++)

    {

        int min_index=i;

        for(int j=i+1;j<n;j++){

            if(a[j]<a[min_index]) min_index=j;

        }

        if(i!=min_index) swap(a[min_index],a[i]);

    }

}

 

 

 

/******************************************************************************************************************

冒泡排序,分为若干趟进行,每一趟排序从前往后比较每两个相邻的元素的大小(因此一趟排序要比较n-1对位置相邻的数)并在每次发现前面的那个数比紧接它后的数大时交换位置

唯一的优化(其实应该说是必须!)就是引入一个标记,若某趟遍历中完全没有进行交换动作,则已然有序

******************************************************************************************************************/

template<typename T>void bubble_sort(T a[],int n)

{

    bool flag=false;

    for(int i=0;i<n;i++){

        for(int j=i;j<n;j++){

            if(a[j]>a[j+1]) {

                swap(a[j],a[j+1]);

                flag=true;

            }

        }

        if(!flag) return;

    }

}

 

 

 

/******************************************************************************************************************

插入排序,每次从数列中取一个还没有取出过的数,并按照大小关系插入到已经取出的数中使得已经取出的数仍然有序

******************************************************************************************************************/

template<typename T>void insertion_sort(T a[],int n)

{

    for(int i=1;i!=n;i++){

        if(a[i]<a[i-1])

        {

            temp = a[i];

            left = 0;  right = i-1;

 

            while (left <= right) {

                mid = (left + right)/2;

                if (temp<a[mid])  right = mid-1;

                else left = mid+1;

            }

 

            for (j = i-1;  j >= left;  j--)  a[j+1] = a[j];

            if (left!= i)  a[left] = temp;

        }

}

 

 

 

/******************************************************************************************************************

堆排序,这是我很喜欢的排序算法之一,《算法导论》还专门列出了一章,时间上保证了严格的n*logn的上界,空间上只需要常数的额外空间。几乎是鱼与熊掌兼得

******************************************************************************************************************/

template<typename T>void adjust(T a[],int i,int n)

{

    int j=2*i;

    T item=a[i];

    while(j<=n){

        if((j<n)&&(a[j]<a[j+1])) j++;

        if(item>=a[j]) break;

        a[j/2]=a[j];

        j*=2;

    }

    a[j/2]=item;

}

 

template<typename T>void heapify(T a[],int n)

{

    for(int i=n/2;i>0;i--) adjust(a,i,n);

}

 

template<typename T>void heap_sort(T a[],int n)//堆排序主函数

{

    heapify(a,n);

    for(int i=n;i>=2;i--){

        swap(a[1],a[i]);

        adjust(a,1,i-1);

    }

}

 

 

/******************************************************************************************************************

希尔排序,这个排序要说清楚还是挺复杂的,它估计是最不稳定的迄今为止,除了在一些特殊的情况下,还没有人能够从理论上分析希尔排序的效率,即便是在Don Knuth的书里我也没找到足够的分析证据。不过有各种基于试验的评估,估计它的时间级从O(N^3/2)O(N^7/6)之间。

Shell排序基本思路是:先取一个小于n的整数d1作为第一个增量,把文件的全部记录分成d1个组。所有距离为dl的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。该方法实质上是一种分组插入方法。

选择间隔序列算是和选择Hash函数一样神奇的魔法,常用的有h=(h-1)/3有一个绝对的条件,就是逐渐缩小的间隔最后一定要等于1,因此最后一趟排序是一次普通的插入排序。

间隔序列中的数字互质通常被认为很重要:也就是说,除了1之外它们没有公约数。这个约束条件使每一趟排序更有可能保持前一趟排序以排好的效果,希尔最初以N/2为间隔的低效率性就是归咎于它没有遵守这个准则。

******************************************************************************************************************/

template<typename T>void shell_insert(T a[],int dis,int n)//dis为当前递增量

{

    for(int i=dis;i<n;i++){

        if(a[i]<a[i-dis]){

            T temp=a[i];

            int j=i-dis;

            do{

                a[j+dis]=a[j];

                j-=dis;

            }while((j>=0)&&(a[j]>temp));

            a[j]=temp;

        }

    }

}

 

template<typename T>void shell_sort(T a[],int n)

{

    int dis=n;

    do{

        dis=dis/3+1;

        shell_insert(a,dis,n);

    }while(dis>0);

}

 

/******************************************************************************************************************

归并排序,这也是我很喜欢的排序算法之一,原因有点八卦,《算法导论》MIT的视频第一堂课就是Charles E. Leiserson讲解的归并排序,很喜欢他的课,然后一鼓作气把主定理(master theory)都看完了,爽YY~~

归并排序时间表现还是很好的,n*logn,主要是空间上花销大了点

******************************************************************************************************************/

template<typename T> void merge(T a[],int left,int partion,int right)

{

    int n1=partion-left+1,n2=right-partion;

    T _L[n1+1],_R[n2+1];

    _L[0]=_R[0]=INT_MAX;

 

    for(int i=1;i<n1;i++) _L[i]=a[left+i-1];

    for(int j=1;j<n2;j++) _R[j]=a[q+j];

 

    _L[n1+1]=_R[n2+1]=INT_MAX;

 

    int ii=jj=1;

    for(int k=left;k<right;k++)

    {

        do{

            if(_L[ii]<=_R[jj]) a[k]=_L[ii++];

            else a[k]=_R[jj++];

        }while((_L[ii]!=INT_MAX)&&(_R[jj]!=INT_MAX));

    }

}

 

template<typename T> void merge_sort(T a[],int left,int right)

{

    if(left<right)

    {

        int mid=(left+right)/2;

        merge_sort(a,left,mid);

        merge_sort(a,mid+1,right);

        merge(a,left,mid,right);

    }

}

 

 

 

/******************************************************************************************************************

快速排序,可能本人有时候喜欢特地和和主流作对吧,快速排序大概是最常用的了。不过我觉得不好,比对排序差劲多了。最坏清醒为On^2),有了这样的最坏清醒,平均性能也就On*logn),哪里能和堆排序比?不过说老实话,真正实际中快速排序还是最快的。

******************************************************************************************************************/

 

template<typename T> int partition(T a[],int first,int last)

{

    T ref=a[last];

    int i=first,j=last;

    do{

        do{i++;}while(a[i]<ref);

        do{j++;}while(a[j]>ref);

        if(i<j) swap(a[i],a[j]);

    }while(i<j);

    a[first]=a[j];

    a[j]=ref;

    return j;

}

 

template<typename T> void quick_sort(T a[],int first,int last)

{

    if(first<last){

        int tmp=partion(a,first,last+1);

        quick_sort(a,first,tmp-1);

        quick_sort(a,tmp+1,last);

    }

}

 

 

 

/******************************************************************************************************************

计数排序,计数排序算法的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。很简单,就不多说了。

******************************************************************************************************************/

template<typename T> void count_sort(T a[],T b[],int n)

{

    T _max=a[0];

    for(int i=1;i<n;i++){

        if(a[i]>_max) _max=a[i];

    }

 

    T_HELP=new T[_max];

    memset(_HELP, 0, _max* sizeof(int));

 

    for(int j=0;j<n;j++)  ++_HELP[a[j]];//使_HELP[i]包含等于i的元素的个数

 

    for(int k=1; k<_max; k++) _HELP[k]=_HELP[k]+_HELP[k-1];//使_HELP[i]包含小于或等于i的元素的个数

 

    for(int t=n-1; t; t--) {

        b[_HELP[a[t]]]=a[t];

        --_HELP[a[t]];

    }

 

    delete[] _HELP;

 

}

 

 

 

/******************************************************************************************************************基数排序(radix sort),基数排序不是那么好写,首先它只适合于整数和字符串但是在应用中它应该是大有价值的,比如我就觉得(不一定正确啊),磁盘上文件的按名称排序就可以用基数排序的思想。

 

桶排序(bucket sort),[01)划分为n个大小相同的子区间,每一子区间是一个桶。然后将n个记录分配到各个桶中。因为关键字序列是均匀分布在[01)上的,所以一般不会有很多个记录落入同一个桶中。由于同一桶中的记录其关键字不尽相同,所以必须采用关键字比较的排序方法(通常用插入排序)对各个桶进行排序,然后依次将各非空桶中的记录连接(收集)起来即可。

 

由于这两种排序都有特别的处理方案,在此不写代码了。

******************************************************************************************************************/

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值