点击蓝字
![2fbde4339838d5c08fca32cfd01f99dc.gif](https://i-blog.csdnimg.cn/blog_migrate/706615ddb1de3152ef1a3c30057d4db8.gif)
关注我们
自学 邓俊辉《数据结构(C++语言版)》第三章——列表,且结合Leetcode题库进行简单练习。? ? ? ? ? ? ? ? 《数据结构(C++语言版)》电子版pdf获取方式: 公众号后台回复【 数据结构】即可获取百度盘链接。Contents
-
1 向量、列表与链表
1.1 向量vector
1.2 列表
1.3 列表是链表的一般化推广
2 ListNode模板类
3 List对象
4 无序列表
5 有序列表
6 列表排序
6.1 插入排序 insertion sort
6.2 选择排序 selection sort
6.3 归并排序
1 向量、列表与链表
1.1 向量vector
数据空间整体创建或销毁,数据元素的物理存储次序与其逻辑次序严格一致,可支持高效的静态操作; 向量支持循秩访问(call-by-rank)的方式,根据数据元素的秩,可以在o(1)时间内直接确定其物理地址;1.2 列表
列表为各元素动态地分配和回收物理地址,在逻辑上相邻的元素记录彼此的物理地址,在逻辑上形成一个整体,支持高效的动态操作,支持循位置访问(call-by-position)的方式。1.3 列表是链表的一般化推广
2 ListNode模板类
// ListNode模板类,即为List数据结构对象中的每一个元素typedef int rank;#define ListNodePosi(T) ListNode* //列表节点位置template <typename T>struct ListNode{ /* data */ T data; ListNodePosi(T) pred; ListNodePosi(T) succ; ListNode(); ListNode(T e, ListNodePosi(T) p = NULL, ListNodePosi(T) s = NULL): data(e), pred(p), succ(s){} ListNodePosi(T) insertAsPred(T const& e); ListNodePosi(T) insertAsSucc(T const& e);};
3 List对象
![d8f8fee9f3d574836371cbeacb0634e9.png](https://i-blog.csdnimg.cn/blog_migrate/76cf3ddabd52908e1db3d24fd426ae8f.png)
void List::init(){
header = new ListNode;
trailer = new ListNode;
header->succ=trailer; header->pred=NULL;
trailer->succ=NULL; header->pred=header;
_size = 0;
}
4 无序列表
由秩到位置的转换:通过秩来指定列节点,可通过重载操作符“[]”:![179c5c609c09f2ae4b73b196d20d4eee.png](https://i-blog.csdnimg.cn/blog_migrate/edc7467e8248a49d6c91da667e474823.png)
![8a99284f17aa60458eb5755fd36b404e.png](https://i-blog.csdnimg.cn/blog_migrate/3279fcc6f3f77be900e93629adda644e.png)
![53f8e46588ab519611401d914702dd75.png](https://i-blog.csdnimg.cn/blog_migrate/ad62a54db878e657dcc2de25cbb959bd.jpeg)
![7bd4631c386c6ad5332b546465edaf07.png](https://i-blog.csdnimg.cn/blog_migrate/b69984a115ff409f7e94f705af26f635.png)
![ef15526221254d903178f937cb0216c0.png](https://i-blog.csdnimg.cn/blog_migrate/473bee0f1f81dd32575494476df30f4c.png)
// 插入节点ListNodePosi(T) ListNode::insertAsPred(T const& e){ ListNodePosi(T) x = new ListNode(e, pred, this); pred->succ = x; pred = x; return x;}
其中的两条语句
pred->succ = x; pred = x; 不能颠倒顺序!!!否则不能成功插入节点,原列表的结构会被破坏。
5 有序列表
有序列表的唯一化:uniquify()![93f56b4aed3e5da01d436fc6b9284eae.png](https://i-blog.csdnimg.cn/blog_migrate/5e24365ccf2fd604db4dde377b3abd48.png)
![fea26b6bc4f2b6789af3ec22bb0e35f4.png](https://i-blog.csdnimg.cn/blog_migrate/62b97b474d28321a40a90ed786ca9754.jpeg)
6 列表排序
6.1 插入排序 insertion sort
插入排序 insertion sort适用于包括向量与列表在内的任何序列结构。 思路: 始终将整个序列视作并切分为两部分,有序的前缀 s[0, r) 和无序的后缀 S[r, n);通过迭代,反复地将后缀的首元素转移到前缀中。 在任何时刻,相对于当前节点 e=S[r], 前缀 S[0, r) 总是业已有序。![81456d2839ccbedd31301e79487bcc21.png](https://i-blog.csdnimg.cn/blog_migrate/0c36787a0c0786f32ee48009ce958e3f.jpeg)
// 插入排序
template //列表的插入排序:对起始于位置 p 的 n 个元素排序
void List::insertionSort( ListNodePosi(T) p, int n) { //valid(p) && rank(p) + n <= size
for (int r=0; r// r为已经排序的那个前缀的长度
// search(e, r, p) 返回 p 的 r 个真前驱中不大于 e 的最后者位置
insertAfter(search(p->data, r, p), p->data);
p = p->succ; //转向下一节点
remove(p->pred);
} // 仅使用o(1)辅助空间,属于就地算法
} //O(n^2)
6.2 选择排序 selection sort
选择排序 selection sort也适用于向量与列表之类的序列结构。 思路: 将序列划分为无序的前缀 S[0, r) 及有序的后缀 S[r, n),此后还要求前缀中的元素都不大于后缀中的元素。 如此,每次只需从前缀中选出最大者,并作为最小元素转移至后缀中,即可使有序部分的范围不断扩张。![5e39eef54a826bab3a1afa167231965e.png](https://i-blog.csdnimg.cn/blog_migrate/82407df70084290c541de51d42d05089.jpeg)
template //列表的选择排序算法,对起始于位置 p 的 n 个元素排序void List::selectionSort ( ListNodePosi(T) p, int n) { //valid(p) && rank(p)+n <= size ListNodePosi(T) head = p->pred; ListNodePosi(T) tail = p; for (int i=0; i tail = tail->succ; //将 head 和 tail 指向排序区列表的 header 和 tailer while( 1//在至少还剩下两个节点之前,在待排序区间内 ListNodePosi(T) max = selectMax( head->succ, n); //找出最大者 insertBefore( tail, remove(max) ); // 将无序前缀中的最大者移到有序后缀中作为首元素 // swap(tail->pred->data, max->data); // 优化:可以不用按上面进行删除和插入操作,只需互换数值即可, 习题 3-13 tail = tail->pred; n--; //n用于记录前缀的宽度 }} //O(n^2)template //从起始于位置 p 的 n 个元素中选出最大者,相同的返回最后者ListNodePosi(T) List::selectMax( ListNodePosi(T) p, int n) { ListNodePosi(T) max = p; //最大者暂定为首节点 p for ( ListNodePosi(T) cur = p; 1 < n; n--) //从首节点 p 出发,将后续节点逐一与 max 比较 if ((cur=cur->succ)->data >= max->data) //若当前元素 >= max, 则 max = cur; return max; //返回最大节点位置} //O(nlogn)
插入排序和选择排序的对比:
有序序列和无序序列次序的颠倒:选择排序中,有序部分是后缀,而无序部分是前缀,且前缀的无序部分都不能超过有序后缀的最小值;而插入排序没有这样的限定。
6.3 归并排序
template //有序列表的归并:当前列表中自 p 起的 n 个元素,与列表 L 中自 q 起的 m 个元素归并void List::merge( ListNodePosi(T) &p, int n, List& L, ListNodePosi(T) q, int m) { //assert: this.valid(p) && rank(p)+n<=_size && this.sorted(p,n) // L.valid(q) && rank(q)+m<=L._size && L.sorted(q,m) //注:在归并排序之类的场合,有可能 this==L && rank(p)+n=rank(q) //为方便归并排序,归并所得的有序列表依然起始于节点 p ListNodePosi(T) pp = p->pred; //方便之后能返回 p while ( 0 < m ) //在 q 尚未移出区间之前 if ( (0data <= q->data) ){ //若 p 仍在区间内且 v(p) <= v(q) if ( q == ( p=p->succ ) ) // 如果此时 p 部分已经处理完,则提前返回 break; n--; // p 归入合并的列表,并替换为其直接后继 } else { //若 p 已超出右界或 v(q) < v(p) 则 ListNodePosi(T) bb = insertB( p, L.remove( (q=q->succ)->pred )); //将 q 转移到 p 之前 m--; } p = pp->succ; //确定归并后区间的起点}template //列表的归并排序算法:对起始于位置 p 的 n 个元素排序void List::mergeSort( ListNodePosi(T) & p, int n) { //valid(p) && rank(p)+n <= _size if (n<2) return; int m = n >> 1; //以中点为界 ListNodePosi(T) q = p; for ( int i=0; i//均分列表 q = q->succ; mergeSort(p, m); mergeSort(q, n-m); //对前后子列表排序 merge(p, m, *this, q, n-m); //归并}//注意:排序后,p 依然指向归并后区间的起点 o(nlogn)
写留言