剑指offer之链表
总结:链表增删改查,重点找到相应的节点,以及特殊情况的处理(链表为空,一个节点情况,尾节点情况)
1 package jzoffer; 2 3 import java.util.ArrayList; 4 import java.util.Scanner; 5 import java.util.Stack; 6 7 public class LinkList 8 { 9 Node head = new Node(); 10 public void addNode(int data){//从末尾添加元素 11 Node newNode = new Node(data),current = head; 12 if(head.next ==null) { 13 head = new Node(); 14 head.next = newNode; 15 } 16 else { 17 while (current.next != null) {//从第一个节点开始判断,非空,再赋值后移 18 current = current.next; 19 } 20 current.next = newNode; 21 } 22 } 23 public void deleteNode(int target){//删除目标元素 24 Node current = head; 25 if (head.next == null) { 26 System.out.println("链表为空,无法删除"); 27 return; 28 } 29 while(current.next != null){ 30 if(current.next.data == target){ 31 current.next = current.next.next; 32 System.out.println("已成功删除节点:"+target); 33 return; 34 } 35 current = current.next; 36 } 37 System.out.println("该元素不存在无法删除!"); 38 } 39 public boolean searchNode(int target){//查询目标元素是否存在 40 Node current = head; 41 if(head.next == null) { 42 System.out.println("链表为空,无法查找!"); 43 return false; 44 } 45 while(current.next != null){ 46 if (current.next.data == target){ 47 System.out.println("查找到节点"+target); 48 return true; 49 } 50 current = current.next; 51 } 52 return false; 53 } 54 55 public void printLinkList(){ 56 Node current = head; 57 if(head.next == null) { 58 System.out.println("链表为空,无法打印"); 59 return; 60 } 61 System.out.println("链表打印:"); 62 while(current.next != null){//从第一个节点开始判断,非空,再赋值后移 63 current = current.next; 64 System.out.println(current.data); 65 } 66 } 67 class Node{ 68 int data; 69 Node next; 70 71 public Node(){ 72 this.data = data; 73 } 74 75 public Node(int data){ 76 this.data = data; 77 } 78 } 79 80 //面试题4:输入一个链表,从尾到头打印链表每个节点的值 81 //利用栈后进先出的特性 82 83 public class ListNode { 84 int val; 85 ListNode next = null; 86 87 ListNode(int val) { 88 this.val = val; 89 } 90 } 91 public ArrayList<Integer> printListFromTailToHead(ListNode listNode) { 92 Stack<Integer> stack = new Stack<>(); 93 while(listNode != null){ 94 stack.push(listNode.val); 95 listNode = listNode.next; 96 } 97 ArrayList<Integer> list = new ArrayList<>(); 98 while (!stack.empty()){ 99 list.add(stack.pop()); 100 } 101 return list; 102 103 } 104 public static void main(String args[]){ 105 LinkList linkList = new LinkList(); 106 Scanner cin = new Scanner(System.in); 107 int num = cin.nextInt(); 108 while(num != -1){ 109 linkList.addNode(num); 110 num = cin.nextInt(); 111 } 112 linkList.deleteNode(5); 113 System.out.println(linkList.searchNode(3)); 114 linkList.printLinkList(); 115 } 116 117 }
补充:
面试题18
题目1:在给定单向链表的头指针和一个节点指针(指向要删除的节点),在O(1)时间删除链表的节点
思路:分为三种情况
1、链表为空或要删除的节点为空,返回提示
2、要删除的节点不是尾节点,将要删除的节点(current)的下一个节点(current.next)的值都赋给current,然后直接删除current
3、要删除的节点是为尾节点,找到尾部节点,正常删除。
题目2:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
思路:设置指针temp指向当前节点,判断若当前节点不等于下一个节点,则将temp节点赋给index,若相等,则将temp移动到不重复节点处再赋给index
1 public ListNode deleteDuplication(ListNode pHead) 2 { 3 ListNode result; 4 ListNode temp=pHead;//当前负责移动判断的节点,这里头结点即为第一个节点 5 ListNode index=new ListNode(1);//指向目前为止未重复的节点 6 index.next=pHead; 7 result=index; 8 while(temp!=null){ 9 if(temp.next!=null&&temp.next.val==temp.val){//判断当前节点和下一个节点是否相等,若相等,则移动并找到不相等为止的节点 10 while(temp.next!=null&&temp.next.val==temp.val){ 11 temp=temp.next; 12 } 13 temp=temp.next;//最后一个重复节点的下一个节点 14 index.next=temp; 15 } 16 else{ 17 index=index.next; 18 temp=temp.next; 19 } 20 } 21 return result.next; 22 }
面试题22:输入一个链表,输出该链表中倒数第k个结点。
思路:设置两个指针first和second,first走到第k个节点的时候,second在第一个节点处,接着同时向后移动
注意特殊情况的处理:
- 链表为空状态,或者k<=0情况,返回空节点
- k大于链表长度的情况,返回空节点,在第一个while循环结束后若k>1则链表长度小于k。
1 public ListNode FindKthToTail(ListNode head,int k) { 2 ListNode first = head,second = head; 3 if(head == null || k <= 0) { 4 return null;} 5 while(k > 1 && first.next != null){ 6 first = first.next; 7 k--; 8 } 9 if(k == 1){ 10 while(first.next != null){ 11 first = first.next; 12 second = second.next; 13 } 14 } 15 else second = null; 16 return second; 17 }
面试题23:链表中环的路口
思路:将问题分为两个步骤(将复杂问题分解成多个步骤)
- 找出环中任意一个节点:设置两个指针fast和low,fast每次走两步,low走一步,当两个指针相遇的时候,一定是在环中相遇。(注:当遇到尾节点的时候说明无环,则返回null)
- 找到环的入口节点:fast回到头结点,low不变,连个结点每次同时走一步,相遇时则为环的入口节点。(可证明)
1 public ListNode EntryNodeOfLoop(ListNode pHead) 2 { 3 ListNode fast = pHead,low = pHead; 4 if(pHead == null || pHead.next == null) return null; 5 while(fast != null && fast.next != null){ 6 low = low.next; 7 fast = fast.next.next; 8 if(low == fast){ //找到相遇节点时 9 fast = pHead; //fast回到头结点 10 while(fast != low){ 11 fast = fast.next; 12 low = low.next; 13 } 14 return low; 15 } 16 } 17 return null; 18 }
面试题24:输入一个链表,反转链表后,输出链表的所有元素
思路:分两种情况(链表为空或只有一个节点,链表不止一个节点)
1 public ListNode ReverseList(ListNode head) { 2 ListNode current = head,pre = null,pnext = null,newHead = null; 3 if(head == null || head.next == null) return head; 4 while(current!= null){ 5 pnext = current.next; 6 if(pnext == null) newHead = current;//一旦发现下一个节点为空,当前节点为最终头结点 7 current.next = pre; 8 pre = current; 9 current = pnext; 10 } 11 return newHead; 12 }
面试题25:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
思路:每次都将两个链表的头部进行比较,小的看成是新的头结点,头节点的下一个节点就是新的merge,使用递归
注意任意一个链表为空的状态
1 public ListNode Merge(ListNode list1,ListNode list2) { 2 if(list1 == null) return list2; 3 if(list2 == null) return list1; 4 ListNode pmHead = null; 5 if(list1!= null && list2 !=null){ 6 if(list1.val <= list2.val){ 7 pmHead = list1; 8 pmHead.next = Merge(list1.next,list2); 9 } 10 else 11 { 12 pmHead = list2; 13 pmHead.next = Merge(list1,list2.next); 14 } 15 } 16 return pmHead; 17 18 }
面试题24:输入两个链表,找出它们的第一个公共结点。(y型)O(m+n):节省空间
思路:分别计算两个链表的长度,根据长度的差值,长的链表先走差值的步数,然后两个链表同时移动对比。
注意空链表情况以及无公共节点的情况。
另一种思路:使用两个栈分别存放两个链表的节点,从尾部向前比较,返回最后一个公共节点。O(m+n)
1 public class Solution { 2 public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { 3 if(pHead1 == null || pHead2 == null) return null; 4 ListNode current = pHead1,current1; 5 int num1 = 0,num2 = 0; 6 while(current != null){ 7 num1++; 8 current = current.next; 9 } 10 current = pHead2; 11 while(current != null){ 12 num2++; 13 current = current.next; 14 } 15 if(num1 > num2) { 16 num1 = num1 - num2; 17 current = pHead1; 18 current1 = pHead2; 19 }else{ 20 num1 = num2 - num1; 21 current = pHead2; 22 current1 = pHead1; 23 } 24 while(num1 > 0){ 25 current = current.next; 26 num1 --; 27 } 28 while(current !=null && current1 !=null){ 29 if(current == current1) return current; 30 else{ 31 current = current.next; 32 current1 = current1.next; 33 } 34 } 35 return null; 36 } 37 }