zoukankan      html  css  js  c++  java
  • LinkedList(链表)

    1.编写代码,移除未排序链表中的重复结点。如果不得使用临时缓冲区,该怎么解决?

    思路:直接迭代访问整个链表,将每个结点加入散列表。若发现有重复元素,则将该结点从链表中移除,然后继续迭代。使用链表。只需扫描一次即可。时间复杂度是o(N),N是链表结点数目。

    import java.util.Hashtable;
    
    class LinkedListNode {
        int data;
        LinkedListNode next;
    }
    
    public class DeleteNodes {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            LinkedListNode head = new LinkedListNode();
                    head.data = 1;
                    LinkedListNode node = new LinkedListNode();
                    node.data = 1;
                    head.next = node;
                    LinkedListNode tmp = head;
                    while (tmp != null) {
                         System.out.println(tmp.data);
                         tmp = tmp.next;
                   }
                   tmp = deleteDups(head);
                   System.out.println("after delete");
                   while (tmp != null) {
                        System.out.println(tmp.data);
                        tmp = tmp.next;
                   }
    
        }
        //使用临时缓冲区
        public static LinkedListNode deleteDups(LinkedListNode n) {
            Hashtable table = new Hashtable();
            LinkedListNode head = n;//保存头结点
            LinkedListNode current = head;
            while (n != null) {
                if (table.containsKey(n.data)) {
                    current.next = n.next;
                } else {
                    table.put(n.data, true);
                    current = n;
                }
                n = n.next;
            }
            return head;
        }
    }
    View Code

    如不借助额外缓冲区,可以用两个指针来迭代:current迭代访问整个链表,runner用于检查后续的结点是否重复。时间复杂度为o(N的平方),空间复杂度为o(1)。

    import java.util.Hashtable;
    
    class LinkedListNode {
        int data;
        LinkedListNode next;
    }
    
    public class DeleteNodes {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            LinkedListNode head = new LinkedListNode();
            head.data = 1;
            LinkedListNode node = new LinkedListNode();
            node.data = 1;
            head.next = node;
            LinkedListNode tmp = head;
            while (tmp != null) {
                System.out.println(tmp.data);
                tmp = tmp.next;
            }
            tmp = deleteDups(head);
            System.out.println("after delete");
            while (tmp != null) {
                System.out.println(tmp.data);
                tmp = tmp.next;
            }
    
        }
    
        //不使用临时缓冲区
        public static LinkedListNode deleteDups(LinkedListNode n) {
            if (n == null) {
                return n;
            }
            LinkedListNode head = n;
            LinkedListNode current = head;
            while (current != null) {
                LinkedListNode runner = current;
                while (runner.next != null) {
                    if (runner.next.data == current.data) {
                        runner.next = runner.next.next;
                    } else {
                        runner = runner.next;
                    }
                }
                current = current.next;        
            }
            return head;
        }
    
    }
    View Code

    1.1删除排序链表中的重复结点

    在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点出现一次,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->3->4->5

    思路:遍历一次,遇到重复的节点删除(将前一节点的指针指向该节点的下一个节点)即可。

    public class Solution {
        public ListNode deleteDuplicates(ListNode head) {
            if (head == null) {
                return null;
            }
            ListNode node = head;
            while (node.next != null)  {
                if (node.val == node.next.val) {
                    node.next = node.next.next;
                } else {
                    node = node.next;
                }
            }
            return head;
        }
    }
    View Code

    1.2删除排序链表中的重复结点II

    在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

    思路:

    1.先给原链表添加一个头结点first方便处理;

    2.使用3个指针,一个指向前一个节点pre,一个指向当前节点pHead,一个指向下一个节点pHead->next。

    3.当当前节点跟后一个节点相等时,不断往后遍历,找到第一个不等于当前节点的节点,然后用 pre 指向它;

    当当前节点跟后一个不相等时,将pre 后移指向pHead,pHead后移一位.。

    public class Solution {
        public ListNode deleteDuplication(ListNode pHead)
        {
            ListNode first = new ListNode(Integer.MIN_VALUE);
            ListNode pre;
            first.next = pHead;
            pre = first;
            while(pHead != null && pHead.next != null) {
                if (pHead.val == pHead.next.val) {
                    int val = pHead.val;
                    while(pHead != null && pHead.val == val) {
                        pHead = pHead.next;
                    }
                    pre.next = pHead;
                } else {
                    pre = pHead;
                    pHead = pHead.next;
                }
            }
            return first.next;
        }
    }
    View Code

    2.实现一个算法,找出单向链表中倒数第k个结点。

    注意:k定义如下:传入k=1将返回最后一个结点,k=2返回倒数第二个结点,以此类推。

    解法1:链表长度已知

    若链表长度已知,倒数第k个结点就是第(length - k)个结点,直接迭代访问链表即可,但是过于简单!

    解法2:递归

    对问题略作调整,只打印倒数第k个结点的值。

    public class NodeLast {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            LinkedListNode head = new LinkedListNode();
            head.data = 1;
            LinkedListNode node = new LinkedListNode();
            node.data = 2;
            head.next = node;
            LinkedListNode node1 = new LinkedListNode();
            node1.data = 3;
            node.next = node1;
            nthToLast(head, 2);
        }
        
        //递归解法
        public static int nthToLast(LinkedListNode head, int k) {
            if (head == null) {
                return 0;
            }
            int i = nthToLast(head.next, k) + 1;
            if (i == k) {
                System.out.println(head.data);
            }
            return i;
        }
    
    }
    View Code

    解法3:迭代:使用双指针p1和p2,并将它们指向链表中相距k个结点的两个结点。【要注意p2移动时i < k - 1】

    先将p1和p2指向链表首结点,然后将p2向前移动k个结点。之后,我们以相同的速度移动这两个指针,p2会在移动length-k步后抵达链表尾结点。此时,p1会指向链表第length-k个结点,或者说倒数第k个结点。

    public class NodeLast {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            LinkedListNode head = new LinkedListNode();
                    head.data = 1;
                    LinkedListNode node = new LinkedListNode();
                    node.data = 2;
                    head.next = node;
                    LinkedListNode node1 = new LinkedListNode();
                    node1.data = 3;
                    node.next = node1;
                    LinkedListNode node2 = nthToLast(head, 2);
                    System.out.println(node2.data);
        }
    
        //迭代解法
        public static LinkedListNode nthToLast(LinkedListNode head, int k) {
            if (head == null || k <= 0) {
                return null;
            }
            LinkedListNode p1 = head;
            LinkedListNode p2 = head;        
            // Move p2 k nodes into the list.  Keep p1 in the same position.
            for (int i = 0; i < k - 1; i++) { 
                if (p2 == null) {
                    return null; // Error: list is too small.
                }
                p2 = p2.next;
            }
            if (p2 == null) { // Another error check.
                return null;
            }
            
            // Move them at the same pace.  When p2 hits the end, 
            // p1 will be at the right element.
            while (p2.next != null) {
                p1 = p1.next;
                p2 = p2.next;
              }
              return p1;
        }
    
    }
    View Code

    3.实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点。

    解法:直接将后继结点的数据复制到当前结点,然后删除这个后续结点。

    public class DeleteNode {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
        }
        
        public static boolean deleteNode(LinkedListNode n) {
            if (n == null || n.next == null) {//n.next==null表示待删除的结点为链表的尾结点,这个问题无解。
                return false;
            }
            LinkedListNode next = n.next;
            n.data = next.data;
            n.next = next.next;
            return true;
        }
    
    }
    View Code

    4.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前。

    解法一:直接创建两个链表:一个链表存放小于x的元素;另一个链表存放大于或等于x的元素。迭代访问整个链表,将元素插入before或after链表。一旦抵达链表末端,则表明拆分完成,最后合并两个链表。注意保存当前操作结点的后继结点然后把当前结点的后续结点置为0这一操作。

    public class Partition {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            LinkedListNode head = new LinkedListNode();
            head.data = 3;
            LinkedListNode node = new LinkedListNode();
            node.data = 2;
            head.next = node;
            LinkedListNode node1 = new LinkedListNode();
            node1.data = 1;
            node.next = node1;
            LinkedListNode tmp = head;
            while(tmp != null){
                System.out.println(tmp.data);
                tmp = tmp.next;
            }
            tmp = partition(head,2);
            System.out.println("after change");
            while(tmp != null){
                System.out.println(tmp.data);
                tmp = tmp.next;
            }
        }
        
        public static LinkedListNode partition(LinkedListNode node, int x) {
            LinkedListNode beforeStart = null;
            LinkedListNode beforeEnd = null;
            LinkedListNode afterStart = null;
            LinkedListNode afterEnd = null;
            while (node != null) {
                LinkedListNode next = node.next;//使用临时结点记下后续结点,以便下一次迭代时使用
                node.next = null;//将当前结点的后继结点置为 0
                if (node.data < x) {
                    if (beforeStart == null) {
                        beforeStart = node;
                        beforeEnd = beforeStart;
                    } else {
                        beforeEnd.next = node;
                        beforeEnd = node;
                    }
                } else {
                    if (afterStart == null) {
                        afterStart = node;
                        afterEnd = afterStart;
                    } else {
                        afterEnd.next = node;
                        afterEnd = node;
                    }
                }
                node = next;
            }
            if (beforeStart == null) {
                return afterStart;
            }
            beforeEnd.next = afterStart;
            return beforeStart;
        }
    
    }
    View Code

    解法二:与第一种解法略有不同,结点不再追加至before和after链表的末端,而是插入这两个链表的前端。

    public class Partition {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            LinkedListNode head = new LinkedListNode();
                    head.data = 3;
                    LinkedListNode node = new LinkedListNode();
                    node.data = 2;
                    head.next = node;
                    LinkedListNode node1 = new LinkedListNode();
                    node1.data = 1;
                    node.next = node1;
                    LinkedListNode tmp = head;
                    while(tmp != null){
                         System.out.println(tmp.data);
                         tmp = tmp.next;
                    }
                    tmp = partition(head,2);
                    System.out.println("after change");
                    while(tmp != null){
                         System.out.println(tmp.data);
                         tmp = tmp.next;
                    }
        }
        
        //解法二
        public static LinkedListNode partition(LinkedListNode node, int x) {
            LinkedListNode beforeStart = null;
            LinkedListNode afterStart = null;
            while (node != null) {
                LinkedListNode next = node.next;//使用临时结点记下后续结点,以便下一次迭代时使用
                if (node.data < x) {
                    //将结点插入before链表的前端
                    node.next = beforeStart;
                    beforeStart = node;
                } else {
                    node.next = afterStart;
                    afterStart = node;
                }
                node = next;
            }
            if (beforeStart == null) {
                return afterStart;
            }
            //找到beforeStart链表的末尾,合并两个链表
            LinkedListNode head = beforeStart;
            while (beforeStart.next != null) {
                beforeStart = beforeStart.next;
            }
            beforeStart.next = afterStart;
            return head;
            
        }
    
    
    }
    View Code

    5.给定两个用链表表示的整数,每个结点包含一个数位。这些数位是反向存放的,也就是个位排在链表首部。编写函数对这两个整数求和,并用链表形式返回结果。假设这些数位是正向存放的,请再做一遍。

    解法:递归地模拟将两个结点的值逐一相加,如有进位则转入下一个结点这一过程。

    class ListNode{
        int data;
        ListNode next;
        ListNode(int x){
            data = x;
        }
    }
    
    public class AddLists {
    
        public static void main(String[] args){
            // TODO Auto-generated method stub
            ListNode head = new ListNode(1);
            ListNode node = new ListNode(2);
            head.next = node;
            ListNode node1 = new ListNode(3);
            node.next = node1;
            ListNode tmp = head;
            while(tmp != null){
                System.out.println(tmp.data);
                tmp = tmp.next;
            }
            tmp = sum(head,head);
            while(tmp != null){
                System.out.println(tmp.data);
                tmp = tmp.next;
            }
        }
    
        public static ListNode sum (ListNode head1, ListNode head2){
            if(head1 == null) return head2;
            if(head2 == null) return head1;
            int n1 = 0;
            int n2 = 0;
            int sum = 0;
            int flag = 0;
            while (head1 != null) {
                n1 += head1.data * Math.pow(10, flag++);
                head1 = head1.next;
            }
            System.out.println("n1 = " + n1);
            flag = 0;
            while (head2 != null){
                n2 += head2.data * Math.pow(10, flag++);
                head2 = head2.next;
            }
            System.out.println("n2 = " + n2);
            sum = n1 + n2;
            System.out.println("sum = " + sum);
            int digit = sum % 10;
            ListNode head = new ListNode(digit);
            ListNode res = head;
            sum /= 10;
            while (sum != 0) {
                digit = sum % 10;
                ListNode n = new ListNode(digit);
                head.next = n;
                head = head.next;
                sum /= 10;
            }
            return res;
        }
    }
    View Code

    进阶解法:

    (1)一个链表的结点可能比另一个链表少,可以一开始先比较两个链表的长度并用零填充较短的链表。

    (2)在前一个问题中,相加的结果不断追加到链表尾部(也即向前传递)。这就意味着递归调用会传入进位,而且会返回结果(随后追加到链表尾部)。

             进阶问题中的结果要加到首部(也即向后传递),递归调用必须返回结果和进位,通过创建一个PartialSum包裹类来解决这一点。

    class ListNode1{
        int data;
        ListNode1 prev;
        ListNode1 next;
        ListNode1(int x, ListNode1 prev, ListNode1 next){
            data = x;
            this.prev = prev;
            this.next = next;
        }
    }
    
    class PartialSum {
        public ListNode1 sum = null;
        public int carry = 0;
    }
    
    public class AddListsMore {
    
        /*public static class PartialSum {
            public ListNode1 sum = null;
            public int carry = 0;
        }*/
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            ListNode1 head = new ListNode1(1, null, null);
            ListNode1 node = new ListNode1(2, null, null);
            head.next = node;
            ListNode1 node1 = new ListNode1(3, null, null);
            node.next = node1;
            
            ListNode1 head1 = new ListNode1(4, null, null);
            ListNode1 node2 = new ListNode1(5, null, null);
            head1.next = node2;
            ListNode1 node3 = new ListNode1(6, null, null);
            node2.next = node3;
            ListNode1 node4 = new ListNode1(7, null, null);
            node3.next = node4;
            
            ListNode1 tmp = new ListNode1(0, null, null);
            tmp = addLists(head,head1);
            while (tmp != null) {
                  System.out.println(tmp.data);
                  tmp = tmp.next;
            }
          
    
        }
        
        public static ListNode1 addLists(ListNode1 l1, ListNode1 l2) {
            int len1 = length(l1);
            int len2 = length(l2);
            //用零填充较短的链表
            if (len1 < len2) {
                l1 = padList(l1, len2 - len1);
            } else {
                l2 = padList(l2, len1 - len2);
            }
            //对两个链表求和
            PartialSum sum = addListsHelper(l1, l2);
            //如有进位,则插入链表首部,否则,直接返回整个链表
            if (sum.carry == 0) {
                return sum.sum;
            } else {
                ListNode1 result = insertBefore(sum.sum, sum.carry);
                return result;
            }
            
            
        }
        //用零填充链表
        private static ListNode1 padList(ListNode1 l, int padding) {
            ListNode1 head = l;
            for (int i = 0; i < padding; i++) {
                ListNode1 n = new ListNode1(0, null, null);
                head.prev = n;
                n.next = head;
                head = n;
            }
            return head;
        }
        //辅助函数,将结点插入链表首部
        private static ListNode1 insertBefore(ListNode1 list, int data) {
            ListNode1 node = new ListNode1(data, null, null);
            if (list != null) {
                list.prev = node;
                node.next = list;
            }
            return node;
        }
        
        private static PartialSum addListsHelper(ListNode1 l1, ListNode1 l2) {
            if (l1 == null && l2 == null) {
                PartialSum sum = new PartialSum();
                return sum;
            }
            //对较小数字递归求和
            PartialSum sum = addListsHelper(l1.next, l2.next);
            //将进位和当前数据相加
            int val = sum.carry + l1.data + l2.data;
            //插入当前数字的求和结果
            ListNode1 full_result = insertBefore(sum.sum, val % 10);
            //返回求和结果和进位值
            sum.sum = full_result;
            sum.carry = val / 10;
            return sum;
        }
        //返回链表长度
        private static int length(ListNode1 l) {
            if (l == null) {
                return 0;
            } else {
                return 1 + length(l.next);
            }
        }
    
    }
    View Code

    6.给定一个有环链表,实现一个算法返回环路的开头结点。

    算法思想:

    (1)创建两个指针:FastPointer和SlowPointer

    (2)SlowPointer每走一步,FastPointer就走两步

    (3)两者碰到一起时,将SlowPointer指向有环链表的头结点,FastPointer保持不变。

    (4)以相同速度移动SlowPointer和FastPointer,一次一步,新的碰撞点即为环路的开头结点。

        public static LinkedListNode FindBeginning(LinkedListNode head) {
            LinkedListNode slow = head;
            LinkedListNode fast = head;
            //找出碰撞处
            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;
            }
            //将slow指向首部,fast不动,两者以相同的速度移动,必定会在环路起始处再次碰撞在一起
            slow = head;
            while (slow != fast) {
                slow = slow.next;
                fast = fast.next;
            }
            return slow;
        }
    View Code

    7.编写一个函数,检查链表是否为回文。可以将回文定义为0->1->2->1->0。

    思路:只需将链表前半部分反转,可以利用栈将前半部分结点入栈来实现。根据链表长度已知与否,入栈有两种方式。

    若链表长度已知,可以用标准for迭代访问前半部分结点,将每个结点入栈。要小心处理长度为奇数的情况。

    若链表长度未知,可以用快慢runner方法迭代访问链表。在迭代循环的每一步,将慢速runner的数据入栈。在快速runner抵达链表尾部时,慢速runner刚好位于链表中间位置。

    这时,栈里就存放了链表前半部分的所有结点,不过顺序是相反的。接下来,迭代访问链表余下结点。每次迭代时,比较当前结点和栈顶元素,若完成迭代时比较结果完全相同,则该链表是回文序列。

        public static boolean isPalindrome(LinkedListNode head) {
            LinkedListNode fast = head;
            LinkedListNode slow = head;
            Stack<Integer> stack = new Stack<Integer>();
            //将链表前半部分元素入栈
            while (fast != null && fast.next != null) {
                stack.push(slow.data);
                slow = slow.next;
                fast = fast.next.next;
            }
            //链表有奇数个元素,跳过中间元素
            if (fast.next != null) {
                slow = slow.next;
            }
            while (slow != null) {
                int top = stack.pop().intValue();
                if (top != slow.data) {
                    return false;
                }
                slow = slow.next;
            }
            return true;
        }
    View Code
  • 相关阅读:
    js string to int
    有的事情是无可奈何的,有的事情是能够改变的……
    拼接字符串去掉最后多余的串,JSON的遍历
    git入门
    js的闭包
    nodejs系列(二)REPL交互解释 事件循环
    nodejs系列(一)安装和介绍
    学习mongo系列(十一)关系
    学习mongo系列(十)MongoDB 备份(mongodump)与恢复(mongorerstore) 监控(mongostat mongotop)
    学习mongo系列(九)索引,聚合,复制(副本集),分片
  • 原文地址:https://www.cnblogs.com/struggleli/p/7828697.html
Copyright © 2011-2022 走看看