简述N种排序算法
排序算法概述
排序算法是程序员日常很常见的算法,基本上每天都会使用排序,在这里将进行一下总结。
排序算法大致可分为比较类排序和非比较类排序二种,其核心区别可以简单的理解为非比较类排序是对比较类排序之前进行了一层包装,也就是说排序的核心操作依然还是比较。
目前主流的排序算法根据分类可以分成下图所示:
简述比较类排序
冒泡排序
冒泡排序是一种老生常谈也是作为一种入门级的排序算法,其逻辑较为简单粗暴,即每次比较相邻的两个元素,如果顺序不符合就交换位置,一直不断的重复直到全部满足则停止。
算法步骤如图所示:
性能:
代码实现:
public class BubbleSort
{
public static List<int> Demo(List<int> data)
{
///你要想看一个人的排序算法什么的写的好不好,主要看他的OP个数
///
//4+4+4+4+4=>20字节
for (var i = ; i < data.Count; i++)//i
{
for (var j = ; j < data.Count - - i; j++)//j
{
if (data[j] > data[j + ])
{
var temp = data[j + ];///空间复杂度
//J+1-->多了一个
data[j + ] = data[j];//->j+1-->
data[j] = temp;
}
}
}
return data;
}
}
BubbleSort
快速排序
由刚刚的冒泡排序可以知道冒泡排序进行了太多次的重复步骤,快排就对这个事情进行了一定的改进。
快排的实现原理也十分简单,首先挑选一个基准数字,然后把比基准数字大的数字都放到一边,然后在这个分区退出后,该基准就应该在数列中间的位置,把这个操作,成为”分区”.
完事以后,再基于基准值元素进行子数列排序就好了.
算法步骤如图所示:
性能:
代码:
public class QuickSort
{
//排序算法---> 遍历-->判断 --->交换<-------降低
public static List<int> Demo(List<int> data, int left = , int? right = null)
{
int len = data.Count, partitionIndex;
right = right ?? data.Count - ;
if (left < right)
{
partitionIndex = partition(data, left, right.Value);
Demo(data, left, partitionIndex - );
Demo(data, partitionIndex + , right);
}
return data;
}
//分块的 左边是啥??? 右边是啥??
static int partition(List<int> data, int left, int right)
{
int pivot = left, index = pivot + ;
for (var i = index; i <= right; i++)
{
if (data[i] < data[pivot])
{
Swap(data, i, index);///交换i跟index
index++;
}
}
Swap(data, pivot, index - );///交换pivot与index-1
return index - ;
} static void Swap(List<int> data, int i, int j)
{
var temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}
QuickSort
简单插入排序
简单插入排序也是一个十分简单的排序,比较容易相像,即拿一个元素出来,看看应该丢哪,然后丢过去即可
原理如图:
性能:
代码:
public class InsertSort
{
public static List<int> Demo(List<int> data)
{
int preIndex, currentIndex;
for (var i = ; i < data.Count; i++)
{
preIndex = i - ;
currentIndex = data[i];
while (preIndex >= && data[preIndex] > currentIndex)
{
data[preIndex + ] = data[preIndex];
preIndex--;
}
data[preIndex + ] = currentIndex;
}
return data;
}
}
InsertSort
希尔排序
这个相对于之前的排序会稍微复杂一点,步骤如下:
1.先把整个序列分成N个子序列.随便选一个序列xyz...增量的哦最好.
2.刚才那个序列增量序列排序一下,直接插入排序
3.循环刚才的事情.然后往前移动.一边移动一边输出.
按图来说就是第一遍循环的时候中间间隔4个,第二遍的时候间隔1个,第三遍的时候紧挨着然后将数移动到合适的位置
如图所示:
性能:
代码:
public class ShellSort
{
public static List<int> Demo(List<int> data)
{
for (int gap =(int) Math.Floor(((decimal)(data.Count / ))); gap > ; gap = (int)Math.Floor(((decimal)(gap / ))))
{
for (var i = gap; i < data.Count; i++)
{
var j = i;
var current = data[i];
while (j - gap >= && current < data[j - gap])
{
data[j] = data[j - gap];
j = j - gap;
}
data[j] = current;
}
}
return data;
}
}
ShellSort
简单选择排序
这个和简单插入有点像但是略有不同。
简单选择排序过程为:
1.找一个最大/小的.
2.放到最后/前.
3.重复并慢慢缩小范围
和简单插入不同在于简单选择先找了最大最小值而简单插入为按顺序一个一个取值。
如图:
性能:
代码:
public class SelectSort
{
public static List<int> Demo(List<int> data)
{
int minIndex, temp;
for (var i = ; i < data.Count - ; i++)
{
minIndex = i;
for (var j = i + ; j < data.Count; j++)
{
if (data[j] < data[minIndex])
{
minIndex = j;
}
}
temp = data[i];
data[i] = data[minIndex];
data[minIndex] = temp;
}
return data;
}
}
SelectSort
堆排序
这个相对而言比较复杂,实现步骤如下:
1.先随便组合成一个二叉树.
2.比较相邻二个数大小来交换相邻的二个节点的顺序,将最大的值向上推,
也就是说父节点大于下面2个各自的子节点而子节点哪个大暂时不关心。
3.将最大的根节点挪移到右下角节点并断开该节点与未排序树的连接
4.重复操作到所有节点均断开连接即排序完成。
如图所示:
性能:
代码:
public class HeapSort
{
static int _count; public static List<int> Demo(List<int> data)
{
Build(data); for (var i = data.Count - ; i > ; i--)
{
Swap(data, , i);
_count--;
Heapify(data, );
}
return data;
}
static void Build(List<int> data)
{ // 建立大顶堆
_count = data.Count;
for (var i = (int)Math.Floor(((decimal)(_count / ))); i >= ; i--)
{
Heapify(data, i);
}
} static void Heapify(List<int> data, int i)
{
int left = * i + ,
right = * i + ,
largest = i; if (left < _count && data[left] > data[largest])
{
largest = left;
} if (right < _count && data[right] > data[largest])
{
largest = right;
} if (largest != i)
{
Swap(data, i, largest);
Heapify(data, largest);
}
} static void Swap(List<int> data, int i, int j)
{
var temp = data[i];
data[i] = data[j];
data[j] = temp;
} }
HeapSort
归并排序
归并排序分为二路归并和多路归并,其实本质思路是一个思路,区别只在于同时有几个并行归并操作而已。
步骤如下:
1.根据你的要求分成你喜欢的数量,形成一个个分区
2.然后每个分区各自排序
3.完事之后合并排序结果,这个步骤类似于合并二个集合,有种学校里学的集合之间合并的感觉。
如图所示:
性能:
代码:
public class MergeSort
{
//P=NP:
public static List<int> Demo(List<int> data)
{
var arr = data.ToArray();
if (data.Count < )
{
return data;
}
int middle = (int)Math.Floor(((decimal)(data.Count / )));
var left = arr.AsSpan().Slice(, middle).ToArray();
var right = arr.AsSpan().Slice(middle).ToArray();
return Merge(Demo(left.ToList()), Demo(right.ToList()));
} static List<int> Merge(List<int> left, List<int> right)
{
var result = new List<int>(); while (left.Count > && right.Count > )
{
if (left[] <= right[])
{
result.Add(left[]);
left.RemoveAt();
}
else
{
result.Add(right[]);
right.RemoveAt();
}
} while (left.Count != )
{
result.Add(left[]);
left.RemoveAt();
} while (right.Count != )
{ result.Add(right[]);
right.RemoveAt();
} return result;
}
}
MergeSort
非比较类排序
计数排序
这个方案也较为简单粗暴,步骤如下:
1.选取最大最小的极值
2.遍历一遍数组,将各个数字出现的次数记录下来
3.根据大小和次数直接生成排序后集合。
如图所示:
性能:
代码:
public class CountingSort
{
public static List<int> Demo(List<int> data)
{
var maxValue = data.Max();
var bucket = new int[maxValue + ];
int sortedIndex = ;
int bucketLen = maxValue + ;
for (var i = ; i < data.Count; i++)
{
if (bucket[data[i]] != )
{
bucket[data[i]] = ;
}
bucket[data[i]]++;
} for (var j = ; j < bucketLen; j++)
{
while (bucket[j] > )
{
data[sortedIndex++] = j;
bucket[j]--;
}
} return data;
}
}
CountingSort
桶排序
针对计数排序进行了改造,步骤如下:
1.预估数据平均大概需要多少个范围容器以及每个容器大概会有多少数据
2.遍历数据,根据数据所在的区间范围丢到对应的桶里
3.排序桶
4.合并数据
如图:
性能:
代码:
public class BucketSort
{
public static List<int> Demo(List<int> data)
{
if (data.Count == )
{
return data;
} var minValue = data[];
var maxValue = data[];
for (var i = ; i < data.Count; i++)
{
if (data[i] < minValue)
{
minValue = data[i];
}
else if (data[i] > maxValue)
{
maxValue = data[i];
}
} var bucketSize = ;
int bucketCount = (int)Math.Floor(((decimal)((maxValue - minValue) / bucketSize))) + ;
var buckets = new List<int>[bucketCount];
for (var i = ; i < buckets.Length; i++)
{
buckets[i] = new List<int>();
} for (var i = ; i < data.Count; i++)
{
buckets[(int)Math.Floor(((decimal)((data[i] - minValue) / bucketSize)))].Add(data[i]);
} var res = new List<int>();
for (var i = ; i < buckets.Length; i++)
{
BubbleSort.Demo(buckets[i]);
for (var j = ; j < buckets[i].Count; j++)
{
res.Add(buckets[i][j]);
}
} return res;
}
}
BucketSort
基数排序
这个排序比较骚的地方就在于运用了数理知识,实现逻辑如下:
1.根据每个数的个位进行排序
2.然后根据十位数进行排序
用桶的思维去看就是总共一定会有10个桶,每次根据不同的规则丢到不同的桶
以2位数为例,由于第一遍的时候所有个位数的数据均排好了顺序,当排好十位数的时候,所有数字顺序就全对上了。
如图:
性能:
代码:
public class RadixSort
{ public static List<int> Demo(List<int> data)
{
var maxDigit = data.Count;
var mod = ;
var dev = ;
for (var i = ; i < maxDigit; i++, dev *= , mod *= )
{
Dictionary<int, List<int>> _counter = new Dictionary<int, List<int>>();
for (var j = ; j < data.Count; j++)
{
var bucket = ((data[j] % mod) / dev);
if (!_counter.ContainsKey(bucket))
{
_counter[bucket] = new List<int>();
}
_counter[bucket].Add(data[j]);
}
var pos = ;
for (var j = ; j <= _counter.Count; j++)
{
if (_counter.ContainsKey(j))
{
while (_counter[j].Count != )
{
data[pos++] = _counter[j][];
_counter[j].RemoveAt();
}
}
}
}
return data;
} }
RadixSort
算法比较
算法的稳定性是什么?
简单而言:如果两个元素值一样,分别用A,B来称呼,排序后这二个元素顺序还是AB而不是BA那就是稳定的。
时间复杂度是什么?
以白话而言:排序的操作总次数,当数据的个数N变化是,操作次数是啥样子的.
空间复杂度是什么?
拿人话说:这算法在算的时候,要申请的内存大小.也就是相对于N的话的多少.所以最少也是O(1)
ps:学习参考源自ben,如有错误,轻拍。
简述N种排序算法的更多相关文章
- 几种排序算法的学习,利用Python和C实现
之前学过的都忘了,也没好好做过总结,现在总结一下. 时间复杂度和空间复杂度的概念: 1.空间复杂度:是程序运行所以需要的额外消耗存储空间,一般的递归算法就要有o(n)的空间复杂度了,简单说就是递归集算 ...
- 秒杀9种排序算法(JavaScript版)
一:你必须知道的 1> JS原型 2> 排序中的有序区和无序区 3> 二叉树的基本知识 如果你不知道上面三个东西,还是去复习一下吧,否则,看下面的东西有点吃力. 二:封装丑陋的原型方 ...
- PHP的几种排序算法的比较
这里列出了几种PHP的排序算法的时间比较的结果,,希望对大家有所帮助 /* * php 四种排序算法的时间与内置的sort排序比较 * 3000个元素,四种算法的排序所用的时间比较 * 冒泡排序 85 ...
- 学习Java绝对要懂的,Java编程中最常用的几种排序算法!
今天给大家分享一下Java中几种常见的排序算法的Java代码 推荐一下我的Java学习羊君前616,中959,最后444.把数字串联起来! ,群里有免费的学习视频和项目给大家练手.大神有空时也 ...
- C#常用8种排序算法实现以及原理简介
public static class SortExtention { #region 冒泡排序 /* * 已知一组无序数据a[1].a[2].--a[n],需将其按升序排列.首先比较a[1]与a[2 ...
- 排序—时间复杂度为O(n2)的三种排序算法
1 如何评价.分析一个排序算法? 很多语言.数据库都已经封装了关于排序算法的实现代码.所以我们学习排序算法目的更多的不是为了去实现这些代码,而是灵活的应用这些算法和解决更为复杂的问题,所以更重要的是学 ...
- java算法03 - 常用的8种排序算法
Java常用的八种排序算法: 插入排序 - 直接插入排序 每次将待排序的记录按照关键字的大小,插入到前面已经排好序的记录的适当位置.直到全部记录插入完成. 代码实现 /** * 直接插入排序 O(n^ ...
- 用 C 语言描述几种排序算法
排序算法是最基本且重要的一类算法,本文基于 VS2017,使用 C 语言来实现一些基本的排序算法. 一.选择排序 选择排序,先找到数组中最小的元素,然后将这个元素与数组的第一个元素位置互换(如果第一个 ...
- 【C++】四种排序算法的时间比较
四种排序算法的时间比较 [注]clock函数对输入(用户输入)元素N排序的计时 #include<iostream> #include<time.h> using namesp ...
随机推荐
- 虚拟机apache启动
/usr/local/apache2/bin/apachectl restart 重启 当启动也行 尝试过进入目录运行,比较奇怪,www目录竟然不一致,直接使用 server httpd start ...
- OpenCV 离散傅立叶变换
#include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" #include ...
- jQuery常用方法归纳总结
转自:http://segmentfault.com/a/1190000000660257 $.grep() $.grep( array, function(elementOfArray, index ...
- Lua 学习 chapter30 编写c函数的技巧 - Jow的博客
目录 数组操作 字符串操作 在c函数中保存状态 生活总需要一点仪式感,然后慢慢的像那个趋向完美的自己靠近. 数组操作 Lua中的数组就是以特殊的方式使用边.像lua_setttable and lua ...
- Cenos配置Android集成化环境, 最终Centos libc库版本过低放弃
To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon ...
- STM32 CAN 发送和接收 寄存器变化过程
发送:
- drf中的jwt使用与手动签发效验
jwt认证 1)session存储token,需要数据库参与,耗服务器资源.低效 2)缓存存token,需要缓存参与,高效,不易集群 3)客户端存token,服务器存签发与交易token的算法,高效, ...
- 吴裕雄--天生自然 R语言开发学习:基本图形(续一)
#---------------------------------------------------------------# # R in Action (2nd ed): Chapter 6 ...
- web虚拟主机的三种配置方法
- python字符串复制的几种方法
>>> list1 = [1,2] >>> id(list1) 50081032 >>> list2 = list1.copy() >> ...