链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。因为在编写程序时,经常用来评估代码能力–包括代码规范性、完整性、鲁棒性。
本文按照论文按照题目进行距离总结,题目列表如下:
1、环形链表插值问题
2、当前访问节点删除问题
3、链表中荷兰国旗问题
4、**单链表的k区间逆序问题
5、复杂结构链表的复制
6、单链表判断是否有环
7、无环单链表判断相交
8、有环单链表相交
9、**单链表相交
内容详述
1、环形链表插值问题
描述:有一个整数val,如何在节点值有序的环形链表中插入一个节点值为val的节点,并且保证这个环形单链表依然有序。
测试样例:{1,2,4,5,7},3
返回结果:{1,2,3,4,5,7}
分析:考察程序的鲁棒性和完整性,要考虑插入值val应当插入的位置:val小于头部节点的值,插入到头部前面;当val大于头部节点值,且小于最小值时,应插入到头部和尾部之间第一比他大的节点之前;当val大于循环链表的最大值,则插入到尾部之后。
示例代码:无
总结:无
2、当前访问节点删除问题
描述:实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。给定带删除的节点,请执行删除操作,若该节点为尾节点,返回false,否则返回true
分析:链表中,要删除一个元素,经常是在知道删除节点的前一个节点的情况下进行操作的。当前只知道要删除节点,因此这个问题只能另寻思路:将该节点的下一个节点的值替换当前值,然后删除后面一个节点,就能完成任务。但是,当删除节点是尾节点时,则不能这么干,也不能直接将当前节点设置为null(这样最会抛出异常ava.lang.StackOverflowError)。
示例代码:无
总结:无
3、链表中荷兰国旗问题
描述:对于一个链表,我们需要用一个特定阈值完成对它的分化,使得小于等于这个值的结点移到前面,大于该值的结点在后面,同时保证两类结点内部的位置关系不变。给定一个链表的头结点head,同时给定阈值val,请返回一个链表,使小于等于它的结点在前,大于等于它的在后,保证结点值不重复。
分析:时间复杂度为O(n)的方法,空间复杂度为O(1)。解题思路是,将链表拆分成两个链表,一个是小于val的链表L1和大于val的链表L2,然后合并两个链表。
示例代码:无
总结:无
4、单链表的k区间逆序问题
描述:有一个单链表,请设计一个算法,使得每K个节点之间逆序,如果最后不够K个节点一组,则不调整最后几个节点。例如链表1->2->3->4->5->6->7->8->null,K=3这个例子。调整后为,3->2->1->6->5->4->7->8->null。因为K==3,所以每三个节点之间逆序,但其中的7,8不调整,因为只有两个节点不够一组。给定一个单链表的头指针head,同时给定K值,返回逆序后的链表的头指针。
分析:使用两个指针pre和lst,分别记录反转区域的头指针和尾指针,当lst和pre相距k时,就对pre和lst界定的范围进行反转。当lst到达尾节点时,就结束了操作。
示例代码:
public ListNode inverse1(ListNode head, int k) {
if (head == null|| k < 2) { return head; }
ListNode pre = head;
ListNode las = head;
ListNode curHead = null;
ListNode preLast = null;
int cnt = k;
while (las != null) {
if (--cnt == 0) {
if (curHead == null) {
curHead = inversek(pre, las);
preLast = pre;
head = curHead;
} else {
curHead = inversek(pre, las);
preLast.next = curHead;
preLast = pre;
}
cnt = k;
if (pre.next == null)
break;
pre = pre.next;
las = pre;
} else {
las = las.next;
}
}
return head;
}
public ListNode inversek(ListNode pre, ListNode las) {
ListNode head = las.next;
ListNode cur = pre;
ListNode nxt = null;
ListNode end = las.next;
while (end != cur) {
nxt = cur.next;
cur.next = head;
head = cur;
cur = nxt;
}
return head;
}
总结:熟练掌握单链表的旋转。
5、复杂结构链表的复制
描述:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点)。
分析:解决思路,首先在原始节点的后面复制一份,形成一个长度为原来两倍的链表;然后,构造random指针,每个新构建的节点的random指向的节点是前一个节点random之后的节点;最后将要返回的结果从构建结果中分离出来。
原始链表
构建结果
示例代码:
public static RandomListNode Clone(RandomListNode pHead) {
if (pHead == null) {
return pHead;
}
// 复制位置节点
RandomListNode ptr = pHead;
while (ptr != null) {
RandomListNode item = new RandomListNode(ptr.label);
item.next = ptr.next;
ptr.next = item;
ptr = ptr.next.next;
}
// 连接random指针
ptr = pHead;
RandomListNode nxt = null;
while (ptr != null) {
nxt = ptr.next;
nxt.random = ptr.random != null ? ptr.random.next : null;
ptr = nxt.next == null ? null : nxt.next;
}
RandomListNode retHead = pHead.next;
RandomListNode cur = pHead;
while (cur != null) {
ptr = cur.next.next;
nxt = cur.next;
cur.next = nxt.next;
nxt.next = ptr == null ? null : ptr.next;
cur = ptr;
}
return retHead;
}
总结:无
6、单链表判断是否有环
描述:如何判断一个单链表是否有环?有环的话返回进入环的第一个节点的值,无环的话返回-1。如果链表的长度为N,请做到时间复杂度O(N),额外空间复杂度O(1)。
分析:设置两个指针fast和slow,fast指针每次走两步,slow指针每次走一步。如果链表无环,fast首先到达尾节点,返回false;否则,fast必然会和slow交于某节点,同时,让fast回到头部,fast和slow每次移动一步,当fast和slow再次重逢的节点就是入环的第一个节点。
示例代码:无
总结:无
7、无环单链表判断相交
描述:现在有两个无环单链表,若两个链表的长度分别为m和n,请设计一个时间复杂度为O(n + m),额外空间复杂度为O(1)的算法,判断这两个链表是否相交。给定两个链表的头结点headA和headB,请返回一个bool值,代表这两个链表是否相交。
分析:分别遍历每个链表得到每个链表的长度lenA和lenB,然后指向较长的链表的指针先走steps(|lenA–LenB|),然后两个链表指针同时移动,当两个指针相等时,说明这两个链表相交,否则不相交。
示例代码:无
总结:无
8、有环单链表相交
描述:如何判断两个有环单链表是否相交?相交的话返回第一个相交的节点,不想交的话返回空。如果两个链表长度分别为N和M,请做到时间复杂度O(N+M),额外空间复杂度O(1)。
分析:如果两个有环链表相交,相交的位置会发生在入环节点之前和入环之后。因此,要求取每个链表的入环节点N1和N2,如果两个节点相等,那么两个有环链表相交;然后,判断连个节点是否环中相交。
示例代码:无
总结:无
9、单链表相交
描述:给定两个单链表的头节点head1和head2,如何判断两个链表是否相交?相交的话返回true,不想交的话返回false。
分析:这个问题总结前几种情况:两个都无环,判断相交;一个有环,另一个无环,绝对不想交;两个都有环,判断相交。
示例代码:无
总结:无