关于链表的一些问题
1. 如果字符串是通过单链表来存储的,那该如何来判断是一个回文串
思路1:用快慢指针先找到中点,然后把后半段链表reversed,然后一个指针在头部,一个指针再中点,开始逐个比较,时间复杂度是O(n)。
思路2:遍历链表,同时将节点入栈;再次遍历链表,同时将元素出栈进行比较,时间复杂度O(n),空间复杂度O(n)。
2. 单链表反转
3. 有序链表合并
4. 链表中环的检测
5. 两个有序的链表合并
6. 删除链表倒数第 n 个结点
思路:创建两个指针,第一个先走n-1步,然后两个指针一起走,当第一个指针走到底部时,第二个就处于倒数第n个。再把这个结点删除。
7.求链表的中间结点
关于写好链表相关代码的一点建议
1.多写多练
2.理解指针或者引用的含义
3.利用哨兵简化实现难度
首先,我们先来回顾一下单链表的插入和删除操作。如果我们在结点 p 后面插入一个新的结点,只需要下面两行代码就可以搞定。
new_node->next = p->next;
p->next = new_node;
但是,当我们要向一个空链表中插入第一个结点,刚刚的逻辑就不能用了。我们需要进行下面这样的特殊处理,其中 head 表示链表的头结点。所以,从这段代码,我们可以发现,对于单链表的插入操作,第一个结点和其他结点的插入逻辑是不一样的。
if (head == null) {
head = new_node;
}
从前面的一步一步分析,我们可以看出,针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理。这样代码实现起来就会很繁琐,不简洁,而且也容易因为考虑不全而出错。如何来解决这个问题呢?
哨兵就要登场了。哨兵,解决的是国家之间的边界问题。同理,这里说的哨兵也是解决“边界问题”的,不直接参与业务逻辑。
还记得如何表示一个空链表吗?head=null 表示链表中没有结点了。其中 head 表示头结点指针,指向链表中的第一个结点。
如果我们引入哨兵结点,在任何时候,不管链表是不是空,head 指针都会一直指向这个哨兵结点。我们也把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫作不带头链表。
我画了一个带头链表,你可以发现,哨兵结点是不存储数据的。因为哨兵结点一直存在,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为相同的代码实现逻辑了。
实际上,这种利用哨兵简化编程难度的技巧,在很多代码实现中都有用到,比如插入排序、归并排序、动态规划等。
4. 留意边界条件的处理
我经常用来检查链表代码是否正确的边界条件有这样几个:
- 如果链表为空时,代码是否能正常工作?
- 如果链表只包含一个结点时,代码是否能正常工作?
- 如果链表只包含两个结点时,代码是否能正常工作?
- 代码逻辑在处理头结点和尾结点的时候,是否能正常工作?
5. 举例画图,辅助思考