目录
面试题6:从尾到头打印链表
题目:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
分析:典型的“后进先出”,可以用栈来实现。
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
Stack<Integer> stack = new Stack();
if(listNode == null)
return list;
ListNode cur = listNode;
while(cur != null){
stack.push(cur.val);
cur = cur.next;
}
while(! stack.empty()){
list.add(stack.pop());
}
return list;
}
面试题18-1:删除链表中重复的节点
题目:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
分析:注意头节点也可能被删除!!!
public ListNode deleteDuplication(ListNode pHead) {
ListNode preHead = new ListNode(0);
preHead.next = pHead;
ListNode pre = preHead;
ListNode cur = pHead;
while(cur != null && cur.next != null){
if(cur.val != cur.next.val)
pre = cur;
else{
while(cur.next != null && cur.next.val == cur.val)
cur = cur.next;
pre.next = cur.next;
}
cur = cur.next;
}
return preHead.next;
}
面试题18-2:在O(1)时间内删除链表节点
题目:给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。
分析:如果要删除的节点i不是尾节点,可先把i的下一个节点j的内容复制,然后把i的指针指向节点j的下一个节点;如果是尾节点,就需要从链表的头节点开始遍历。注意:如果链表只有一个节点,即删除的节点既是头节点也是尾节点,需要将头指针设置为null。
面试题22:链表中倒数第k个结点
题目:输入一个链表,输出该链表中倒数第k个结点。
分析:假设链表中有n个结点,则倒数第k个结点也就是第n-k+1个结点。定义两个指针,第一个指针先走k-1步,在走第k步时,第二个指针也从头开始遍历。由于两个指针始终相差k-1步,当第一个指针走到最后一个结点时,第二个指针刚好指向第n-k+1个结点。注意讨论结点为null以及k为0的情况。
public ListNode FindKthToTail(ListNode head,int k) {
if(head == null || k == 0)
return null;
ListNode quick = head,low = head;
int i = 1;
while(i < k && quick != null){
quick = quick.next;
i++;
}
while(quick != null && quick.next != null){
quick = quick.next;
low = low.next;
}
if(quick == null)
return null;
return low;
}
面试题23:链表中环的入口节点
题目:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
分析:先用快慢指针来确定链表中是否包含环;在找到环中任意一个节点之后,可确定环中的节点数目k,我们先定义两个指针p1、p2均指向链表的头节点,如果让指针p1先走k步,再两个指针同时走,当p2到达环的入口节点时,p1已经围绕环走了一圈,又回到了入口节点,也就是说指针p1、p2相遇时即为环的入口节点。
public ListNode EntryNodeOfLoop(ListNode pHead) {
ListNode slow = pHead,fast = pHead;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast)
break;
}
if(fast == null || fast.next == null)
return null;
int loop = 1;
fast = fast.next;
while(slow != fast){
fast = fast.next;
loop++;
}
slow = pHead;fast = pHead;
while(loop > 0){
fast = fast.next;
loop--;
}
while(slow != fast){
fast = fast.next;
slow = slow.next;
}
return fast;
}
面试题24:反转链表
题目:输入一个链表,反转链表后,输出新链表的表头。
分析:定义三个指针,分别指向当前遍历到的节点、它的前一个节点以及后一个节点。常犯错误:返回的反转之后的头节点不是原始链表的尾节点。
循环实现:
public ListNode ReverseList(ListNode head) {
if(head==null || head.next == null)
return head;
ListNode pNode = head;
ListNode pPrev = null;
while(pNode!=null){
ListNode pNext = pNode.next;
pNode.next = pPrev;
pPrev = pNode;
pNode = pNext;
}
return pPrev;
}
递归实现:
public ListNode ReverseList(ListNode head) {
if(head==null || head.next == null)
return head;
ListNode temp = head.next;
head.next = null;
ListNode newHead = ReverseList(temp);
temp.next = head;
return newHead;
}
面试题25:合并两个排序的链表
题目:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
分析:递归求解即可。
public ListNode Merge(ListNode list1,ListNode list2) {
if(list2 == null)
return list1;
if(list1 == null)
return list2;
ListNode head = null;
if(list1.val <= list2.val){
head = list1;
head.next = Merge(list1.next,list2);
}
else{
head = list2;
head.next = Merge(list1,list2.next);
}
return head;
}
面试题35:复杂链表的复制
题目:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
分析:第一步,根据原始链表的每个结点N创建对应的N',并且将N’链接在N的后面;
第二步,设置复制出来的结点的m_pSibling。假设原始链表上的N的m_pSibling指向结点S,那么其对应复制出来的N’是N的m_pNext指向的结点,同样S’也是S的m_pNext指向的结点;
第三步,将这个长链表拆分成两个链表。
public RandomListNode Clone(RandomListNode pHead){
cloneNodes(pHead);
connectRandomNodes(pHead);
return reconnectNodes(pHead);
}
private void cloneNodes(RandomListNode pHead) {
RandomListNode cur = pHead;
while(cur != null){
RandomListNode clone = new RandomListNode(cur.label);
clone.next = cur.next;
cur.next = clone; //将N’链接在N的后面
cur = clone.next;
}
}
private void connectRandomNodes(RandomListNode pHead) {//设置复制出来的结点的random
RandomListNode cur = pHead;
while(cur != null){
RandomListNode clone = cur.next;
if(cur.random != null){
clone.random = cur.random.next;
}
cur = clone.next;
}
}
private RandomListNode reconnectNodes(RandomListNode pHead) {
RandomListNode cur = pHead;
RandomListNode cloneHead = null;
RandomListNode cloneNode = null;
if(cur != null){
cloneHead = cloneNode = cur.next;
cur.next = cloneNode.next;
cur = cur.next;
}
while(cur != null){
cloneNode.next = cur.next;
cloneNode = cloneNode.next;
cur.next = cloneNode.next;
cur = cur.next;
}
return cloneHead;
}
面试题52:两个链表的第一个公共结点
题目:输入两个单链表,找出它们的第一个公共结点。
分析:如果两个单链表有公共节点,那么这两个链表从某一节点开始,它们的next指针都指向同一个节点,因此它们之后的节点都是重合的。可以首先遍历两个链表得到链表的长度得到长度差x,然后在较长的链表上先走x步,再同时在两个链表上遍历,找到第一个相同的节点就是它们的第一个公共节点。
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null||pHead2 == null) {
return null;
}
int count1 = countLen(pHead1);
int count2 = countLen(pHead2);
ListNode shortHead = pHead1,longHead = pHead2;
if(count1 > count2){
longHead = pHead1;shortHead = pHead2;
}
int flag = Math.abs(count1 - count2);
while(flag > 0){
longHead = longHead.next;
flag--;
}
while(shortHead != longHead){
shortHead = shortHead.next;
longHead = longHead.next;
}
return shortHead;
}