稳定性
一般数组排序这种基础类型情况其实排序的稳定性没有什么用处,因为单独的一个数字是没有什么意义的。稳定性主要是用在非基础的情况,比如说结构体中有多个元素,那么我们按照不同的类型排序,那么按照一个排序之后,再按照另外一个排序,另一个排序里面也会有前一个排序的作用(先成绩后班级==一个班里成绩也是单调的)
选择排序显然是不稳定的。
冒泡排序可以是稳定的。(相等的时候不让他换就可以了)
插入排序也可以是稳定的。
归并排序可以是稳定的(相等的时候先考虑左边的)
快排是做不到稳定的
堆排也是做不到稳定的(这个是显而易见的,他只鸟自己的二叉树结构,生成大小根堆的时候,就破坏了因为他只会和自己的父子节点进行比较)
基数排序,或者说不基于比较的排序是很容易做到稳定的。
另外,除非是有空间的限制,不然一般不要用堆排序,快排是最快的。
基于比较的情况下,排序的时间复杂性到头了是nlogn,而如果要实现稳定性,空间复杂性到头了是N(保证前面的时间复杂性的条件下)
快排的大小比较排序是经典的0-1排序,所以如果要奇偶分类的话,也是可以做到的。但是如果还是要稳定性,那就是有病,直接骂过去就可以了。
大样本情况下使用快排的思想来调度,在小样本的情况下,可以直接用插入排序就可以了,因为虽然是O(n2)的时间复杂度,不过插入的常数时间是低的。这就是综合排序,一种优化的快排。如果把插入改成归并的话,那就是为了稳定性。因为不同的排序各有各的优势。
顺便说一下自带排序的数据结构
set和map本质上没有什么区别,结构是一样的,就是map会多带一个value(另外,这里面都是不能有重复的key的内容的),另外增删改查的操作都是可以理解成常数时间的操作。
这几种结构对于传统的数据类型一般都是值传递的,直接copy一个放进哈希表,但是如果是struct之类的结构,那就是8字节,就存这个东西的内存地址。这里我们就会注意到一点,那就是此时,即便是两个内容一样但是地址不同的结构体也是可以进表的,因为内存地址不同,而内存地址才是key
相比较与哈希表,有序表的功能多一些,自带排序,有序组织(甚至可以找接近某内容的元素),但是性能差一点
Ps:如果不是基础类型的东西放进有序表的话,你是必须要提供比较器的,因为有序表自己内部是需要比较排序的
终于到了今天的内容——链表了
链表的基本结构大家应该都懂,就不过多赘述了,我这里就直接列一下链表的相关的一些情况,讲一讲如何应用列表解决问题:
判断回文链表的方法
可以用栈,一个个填进去,然后直接一个出栈,一个遍历链表去比较。这样是利用栈的先进后出实现反向链表,有一个不一样,就不是回文。
此法虽好,但是栈的空间还是有点大了。
我们可以畅想一下,就是如果我们能够直接把回文的右半段放进栈里,是不是就能好很多,直接就是右半段比左半端
那我们怎么做呢?快慢指针的思路,快指针到头了的时候,慢指针右边的都可以直接进栈,快指针一次走两步。
但是这里我们发现,对于长度为奇数或者偶数的链表,是要求不一样的,奇数的时候,希望慢指针是标准中点,偶数的时候,希望慢指针是中间两个里左边的那个。
那我们可以只用有限的几个变量实现吗?
可以,慢指针在快指针走完之后来到中间,然后,把慢指针剩下的内容直接逆序。从两头来分别对比。
对链表进行partion(就是快排中一次递归做到的事情),可以用node数组,也可以用六个node引用小于的头尾,等于的头尾,大于的头尾
rand链表克隆
可以使用map结构(或者说哈希表),一个老节点,一个新节点,然后遍历老节点,修改遍历的老节点对应的新节点的内容即可。
不用哈希表可以吗?
也可以,就是整点花活。就是123链表变化称为11’22’33’,直接克隆节点跟在后面,每一次一对一对遍历就可以,只处理rand指针,然后分离出来