当前课程知识点:数据结构(上) > 第三章 列表 > (b)无序列表 > 03B-5 唯一化
接下来考察列表的唯一化问题
也就是如何将列表中
可能存在的重复元素剔除掉
在我们接下来将要介绍的这个算法中
我们始终将整个列表视作由三部分组成
这个前缀部分
作为这个算法的一条不变性
已经能够保证不包含任何重复的节点
而我们当前所要考察的呢
就是接下来的这个节点
不妨将它称作p
当然 此时可能还存在一个非空后缀
我们这个算法的关键部分是
如果当前那个节点的数值为e
我们就以e为目标
在前缀中对它进行查找
无论查找成功与否
我们都可以将问题的规模
有效地降低至少一个单位
具体的算法可以实现为这样一段代码
我们来解读一下
首先是处理平凡情况
确保这个列表中至少包含两个节点
接下来 这一步可以认为是初始化
我们可以看到p最初的时候
是指向首节点
也就是说 这个时候的前缀
实际上可以认为是空的
所以刚才我们所说的
不变性自然也就满足
那么接下来
算法的主体部分是这个循环
可以看到 每一次我们都将
p中所存的那个元素
这就是e 作为查找的对象
那么查找的范围在哪呢?
是以p为基准的r个真前驱
那么这个r是多少呢?
这个实际上也是这个算法的一个不变性
我们说 r在任何时候其实就等于
整个这个前缀的长度
换而言之 也就是在整个
这个前缀中进行查找
无非两种情况
一种就是这个find这个操作
返回了一个非空的元素
它的数值当然就等于e
在这里呢 我们是以q来指向它的
这个时候 大家可以看到
我们将这两个重复元素中的一个
具体来说 也就是这个q删除掉
否则的话呢 如果q是一个null呢?
那就意味着查找失败
换而言之
在这样的一个前缀中根本就不存在
语义相同的元素
所以在这个时候 p这个节点
可以归入这样的一个前缀中去
使得这个前缀拓展一个单位
在这种情况下
我们就可以将r加1
也就是说 前缀的长度
增加一个单位
同时p转入它的后继元素
继续迭代
直到抵达哨兵trailer
也就意味着整个列表全部扫描
并且处理完毕
细心的同学可能已经注意到
这里如果发现了一个q
与刚才那个p相同的话
我们倾向于去删除q
既然它们数值都一样
为什么我们不去删除p呢?
在此大家不妨暂停一下
略加思考 然后再继续
是的
我想你应该已经找到答案了
如果我们不是删除q 而是删除p
表面上看是等价的
但是在接下来的这步迭代中
我们首先要将p转向p的后继这个操作
就存在风险
因为p这个时候 实际上已经
没有一个明确的指向
很有可能会发生错误
所以相对而言
删除q是更安全的一个方法
-选课之前
--写在选课之前
--宣传片
-考核方式
--考核方式
-OJ系统说明
--关于OJ
--1-注册与登录
--2-界面与选课
--3-提交测试
-关于课程教材与讲义
--课程教材与讲义
-关于讨论区
--关于讨论区
-微信平台
--html
-PA晋级申请
--PA晋级
-(a)计算
--演示
--(a)计算--作业
-(b)计算模型
-(b)计算模型--作业
-(c)大O记号
-(c)大O记号--作业
-(d)算法分析
-(d)算法分析--作业
-(e)迭代与递归
-(e)迭代与递归--作业
-(xc)动态规划
-- 演示
-(xc)动态规划--作业
-本章测验--作业
-(a)接口与实现
--02A-5 复制
-(a)接口与实现--作业
-(b)可扩充向量
-(b)可扩充向量--作业
-(c)无序向量
--02C-1 概述
--02C-3 插入
--02C-6 查找
--02C-8 遍历
-(c)无序向量--作业
-(d1)有序向量:唯一化
-(d1)有序向量:唯一化--作业
-(d2)有序向量:二分查找
-(d2)有序向量:二分查找--作业
-(d3)有序向量:Fibonacci查找
-(d3)有序向量:Fibonacci查找--作业
-(d4)有序向量:二分查找(改进)
-(d4)有序向量:二分查找(改进)--作业
-(d5)有序向量:插值查找
-第二章 向量(下)--(d5)有序向量:插值查找
-(e)起泡排序
--02E-2 改进
--02E-3 反例
-(e)起泡排序--作业
-(f)归并排序
-(f)归并排序--作业
-本章测验--作业
-(a)接口与实现
--03A-4 实现
-(a)接口与实现--作业
-(b)无序列表
--03B-2 查找
-(b)无序列表--作业
-(c)有序列表
--03C-3 查找
-(c)有序列表--作业
-(d)选择排序
--03D-1 构思
--03D-2 实例
--03D-3 实现
--03D-4 推敲
--03D-6 性能
-(d)选择排序--作业
-(e)插入排序
--03E-1 经验
--03E-2 构思
--03E-3 对比
--03E-4 实例
--03E-5 实现
-(e)插入排序--作业
-(xd)习题辅导:LightHouse
-本章测验--作业
- (a)栈接口与实现
--04A-1 栈
--04A-2 实例
--04A-3 实现
- (a)栈接口与实现--作业
-(c1)栈应用:进制转换
-第四章 栈与队列--(c1)栈应用:进制转换
-(c2)栈应用:括号匹配
-(c2)栈应用:括号匹配--作业
-(c3)栈应用:栈混洗
-第四章 栈与队列--(c3)栈应用:栈混洗
-(c4)栈应用:中缀表达式求值
-(c4)栈应用:中缀表达式求值--作业
-(c5)栈应用:逆波兰表达式
-第四章 栈与队列--(c5)栈应用:逆波兰表达式
-(d)队列接口与实现
--04D-1 接口
--04D-2 实例
--04D-3 实现
-第四章 栈与队列--本章测验
-(a)树
--05A-1 动机
--05A-2 应用
-(a)树--作业
-(b)树的表示
--05B-2 父亲
--05B-3 孩子
-第五章 二叉树--(b)树的表示
-(c)二叉树
-(c)二叉树--作业
-(d)二叉树实现
-(d)二叉树实现--作业
-(e1)先序遍历
-(e1)先序遍历--作业
-(e2)中序遍历
-第五章 二叉树--(e2)中序遍历
-(e4)层次遍历
-第五章 二叉树--(e4)层次遍历
-(e5)重构
-(e5)重构--作业
-本章测验--作业
-(a)概述
-(a)概述--作业
-(b1)邻接矩阵
-(b1)邻接矩阵--作业
-(c)广度优先搜索
--06C-2 策略
--06C-3 实现
--06C-5 实例
-(c)广度优先搜索--作业
-(d)深度优先搜索
--06D-1 算法
--06D-2 框架
--06D-3 细节
-(d)深度优先搜索--作业
-第六章 图--本章测验