简述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 ...
随机推荐
- 蓝桥杯-2016CC-卡片换位
卡片换位 你玩过华容道的游戏吗?这是个类似的,但更简单的游戏.看下面 3 x 2 的格子 在其中放5张牌,其中A代表关羽,B代表张飞,* 代表士兵.还有一个格子是空着的. 你可以把一张牌移动到相邻的空 ...
- C语言学习笔记之动态分配数组空间
本文为原创文章,转载请标明出处 高级语言写多了,再拿起C语言的时候,自己已经傻了... C语言中数组大小不能为变量,即使这个变量已经被赋过值了,应该使用malloc方法进行数组空间动态分配. 如下: ...
- Mybatis/ibatis基础知识
Tip:mapper.xml中sql语句不允许出现分号! 1.#和$符号的区别 #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号.如:order by #user_id#,如果传入的值是 ...
- POJ 2553 The Bottom of a Graph Tarjan找环缩点(题解解释输入)
Description We will use the following (standard) definitions from graph theory. Let V be a nonempty ...
- java集合 list与Set、Map区别
1.List,Set都是继承自Collection接口,Map则不是. 2.List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉 ,(注意:元素虽 ...
- xampp安装后启动apache出现端口占用问题
apache默认监听电脑80端口,当端口被占用时,xampp无法正常启动apache.我们需要将端口解除占用再启动. xampp报错: Problem detected!19:36:24 [Apach ...
- IDEA 详细使用教程
第一步:安装 略 安装首先需要Java语言的运行环境,这里不做概述. 第二步:破解 这里使用的是2016版的,所以直接在网上搜索秘钥即可 https://blog.csdn.net/ksksjipen ...
- 杂记:VMware中为mac虚拟机扩容
之前在VMware中安装Mac虚拟机时,硬盘选的是默认的40G,后来用的过程中随着软件的安装,特别是安装完Xcode和QT5.9之后,可用空间只剩不到3G,每次开机之后都会提醒空间不足,需要清理空间, ...
- fabric 初步实践
在集群部署时,我们经常用到堡垒机作为跳板,堡垒机和集群的其他的用户名.密码.端口号都是不同的,fabric如何进行配置不同的用户.端口号和密码. fabric作为一种强大的运维工具,可以让部署运维轻松 ...
- Blue的博客
整合其他ORM框架 使用Spring所提供的ORM整合方案, 可以获得许多好处: 方便基础设施的搭建 Spring中, 对不同的ORM框架, 首先, 始终可以采用相同的方式配置数据源; 其次, Spr ...