数据结构-链表

链表

  1. 请说出集中基本的数据结构。

常见的基本数据结构有链表、栈、队列、树。

  1. 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑结构是通过链表中的指针连接次序实现的。链表是由一系列节点这组成,这些节点不必在内存中相连,每个节点由数据部分Data和链部分Next组成,Next指向下一个节点,这样当添加或删除时,只需要改变相关节点的Next的指向,效率较高。【链表在内存上不连续,字逻辑上连续】
  2. 栈是限制插入和删除智能在一个位置上进行的表,【后进先出】
  3. 队列只允许在front端进行删除操作,在rear端进行插入操作。【先进先出】
  4. 树是一种非线性数据结构,主要考察二叉树。它是由n(n>=1)个有限结点组成一个具有层次关系的集合。
  1. 怎么判断链表有环,怎么找环节点?
    思路:主要考察两个知识点:判断链表是否为环;如果有环,如何找到这环的入口。
    (1)判断是否有环;
    使用快慢指针,分别定义fast和slow指针,从头结点出发,fast指针每一移动两个节点,slow指针移动一个节点,若fast和slow在途中相遇,则链表有环。
    2)如果有环,如何找到这个环的入口:
    两个指针,一个从链表头开始走,一个从环内快慢指针相交的节点走;
    那么当这两个指针相交的时,此时相交的节点就是环入口。
package 链表;
public class demo1 {

    /**
     * [题目] 一个链表中包含环,请找出该链表的环的入口结点。
     */
    public static void main(String[] args) {

        Node node = createTestNode();
        Node nodeBySlowFast = isCircleBySlowFast(node);
        System.out.println("isCircleBySlowFast : "
                + ((nodeBySlowFast == null) ? "这不是一个环链表" : ("当前环链表的入口是: " + nodeBySlowFast.tag)));
    }

    public  static Node createTestNode() {
        Node node12 = new Node(null, 12);
        Node node11 = new Node(node12, 11);
        Node node10 = new Node(node11, 10);
        Node node9 = new Node(node10, 9);
        Node node8 = new Node(node9, 8);
        Node node7 = new Node(node8, 7);
        Node node6 = new Node(node7, 6);
        Node node5 = new Node(node6, 5);
        Node node4 = new Node(node5, 4);
        Node node3 = new Node(node4, 3);
        Node node2 = new Node(node3, 2);
        // 设置环入口
        node12.next = node5;
        Node head = new Node(node2, 1);
        return head;
    }

    /**
     * 网上做法,其实也是最难的思路:
     * 难度不在于编码,而在于不借助于除了 Node 之外的思路,寻找环的入口 !!
     * 判断是否是有环链表:一个快指针和一个慢指针,在环中,快指针肯定会反向追上慢指针。
     * 寻找环入口:
     * 两个指针,一个从链表头开始走,一个从环内快慢指针相交的节点走;
     * 那么当这两个指针相交的时,此时相交的节点就是环入口。
     * PS:这个寻找环入口的思路,并不容易,甚至于要理解也是有点困难的, 我自己也没有想到方法,只能引用别人的博客中的思路。
     *
     * @param node
     * @return
     */
    public static Node isCircleBySlowFast(Node node) {
        Node slow = node;
        Node fast = node;
        Node p = node;
        while (slow != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast) {
                Node q = fast;
                while(p != q) {
                    p = p.next;
                    q = q.next;
                }
                return p;
            }
        }
        return null;
    }
    public static class Node {

        // 这仅仅是一个标识,用来标记这是哪一个节点
        public int tag;

        public Node next;

        public Node(Node next, int tag) {
            this.next = next;
            this.tag = tag;
        }

        // 逻辑断开 思路需要的tag
        public boolean hasNext = true;
    }
}
  1. 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
    思路:
    1. 定义虚拟节点
    2. 定义fast指针heslow指针,初始值为虚拟头节点。 在这里插入图片描述fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
      在这里插入图片描述
      fast和slow同时移动,直到fast指向末尾,如题:
      在这里插入图片描述
      删除slow指向的下一个节点
      代码实现:
      /**
       * Definition for singly-linked list.
       * public class ListNode {
       *     int val;
       *     ListNode next;
       *     ListNode() {}
       *     ListNode(int val) { this.val = val; }
       *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
       * }
       */
      class Solution {
          public ListNode removeNthFromEnd(ListNode head, int n) {
              ListNode dummy = new ListNode(-1);
              dummy.next = head;
      
              ListNode slow = dummy;
              ListNode fast = dummy;
              while (n-- > 0) {
                  fast = fast.next;
              }
              // 记住 待删除节点slow 的上一节点
              ListNode prev = null;
              while (fast != null) {
                  prev = slow;
                  slow = slow.next;
                  fast = fast.next;
              }
              // 上一节点的next指针绕过 待删除节点slow 直接指向slow的下一节点
              prev.next = slow.next;
              // 释放 待删除节点slow 的next指针, 这句删掉也能AC
              slow.next = null;
      
              return dummy.next;
          }
      }
      
  2. 如何判断一个单向链表存在回路?(请问如何判断一个链表是否有环)
    方法1:用一个指针数组A,存储已访问过的节点。用一个指针p,每次都在链表上移动一步,然后与指针数组A比较,若数组中没有指针与p相同,说明第一次访问p,将p放入数组中;若有指针与p相同,则存在环路,且第一次相同的节点为环的入口。(链表长度为n,则需要空间o(n),且每次要与指针数组比较,时间复杂度为 O(n^2)。)
    方法2:用两个指针(fast,slow),慢指针一次跳一步,快指针一次条两步,如果块的能追上慢的就表示有环。
  3. 如何判断两个链表是否相交?
    从头遍历两个链表。创建两个栈,第一个栈存储第一个链表的节点,第二个栈存储第二个链表的节点,每遍历到一个节点,就将该节点入栈。两个节点都入栈后,则通过top判断栈顶的节点是否都相等即可判断两个链表是否相交。若两链表相交,则循环出栈,直到遇到两个出栈的节点不相同,则这个节点的后一个节点就是第一个相交的节点。
    java代码:
    public class Solution {
        public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            ListNode curA = headA;
            ListNode curB = headB;
            int lenA = 0, lenB = 0;
            while (curA != null) { // 求链表A的长度
                lenA++;
                curA = curA.next;
            }
            while (curB != null) { // 求链表B的长度
                lenB++;
                curB = curB.next;
            }
            curA = headA;
            curB = headB;
            // 让curA为最长链表的头,lenA为其长度
            if (lenB > lenA) {
                //1. swap (lenA, lenB);
                int tmpLen = lenA;
                lenA = lenB;
                lenB = tmpLen;
                //2. swap (curA, curB);
                ListNode tmpNode = curA;
                curA = curB;
                curB = tmpNode;
            }
            // 求长度差
            int gap = lenA - lenB;
            // 让curA和curB在同一起点上(末尾位置对齐)
            while (gap-- > 0) {
                curA = curA.next;
            }
            // 遍历curA 和 curB,遇到相同则直接返回
            while (curA != null) {
                if (curA == curB) {
                    return curA;
                }
                curA = curA.next;
                curB = curB.next;
            }
            return null;
        }
    }
    
  4. 循环链表的增添,删除,插入,修改,查询
    public class CycleLinkedList {
     
        class CycleNode {
            Object data;
            CycleNode next = null;
            CycleNode() {//为空情况
                this.data = null;
            }
            CycleNode(Object data) {//不为空的情况
                this.data = data;
            }
        }
     
        private CycleNode head, pos;
        private int size;
     
        //初始化时,生成一个包含头节点的空链表
        CycleLinkedList() {
            head = new CycleNode();
            head.next = head;
            pos = head;
            this.size = 0;
     
        }
     
        /**
         * 返回链表的大小
         * @return
         */
        public int len() {
            return this.size;
        }
     
        /**
         * 判断链表是否为空
         * @return
         */
        public boolean isEmpty() {
            return this.size == 0;
        }
     
        /**
         * 添加元素,在末尾节点处添加
         */
        public void add(Object data) {
            CycleNode temp = new CycleNode(data);
            if (head.next == head) {
                head.next = temp;
                temp.next = head;
            } else {
                pos = head;
                while (pos.next != head) {
                    pos = pos.next;
                }
                pos.next = temp;
                temp.next = head;
            }
            size++;
        }
     
        /**
         * 插入元素, 在指定索引位置处插入
         * @param index
         * @param data
         */
        public void insert(int index, Object data) {
            CycleNode temp = new CycleNode(data);
            if (index < 0 || index > size - 1)
                throw new RuntimeException("索引错误" + index);
            int i = 0;
            pos = head;
            while (pos.next != head && i < index) {
                pos = pos.next;
                i++;
            }
            temp.next = pos.next;
            pos.next = temp;
            size++;
        }
     
        /**
         * 移除元素
         */
        public Object remove(int index) {
            Object value = null;
            if (index < 0 || index > size - 1)
                throw new RuntimeException("索引错误" + index);
            int i = 0;
            pos = head;
            while (pos.next != head && i < index) {
                pos = pos.next;
                i++;
            }
            value = pos.next.data;
            pos.next = pos.next.next;
            size--;
            return value;
        }
     
        /**
         * 替换元素
         */
        public Object replace(int index, Object newObj) {
            Object value = null;
            if (index < 0 || index > size - 1)
                throw new RuntimeException("索引错误" + index);
            int i = 0;
            pos = head;
            while (pos.next != head && i < index) {
                pos = pos.next;
                i++;
            }
            value = pos.next.data;
            pos.next.data = newObj;
            return value;
        }
     
        /**
         * 获取元素
         */
        public Object get(int index) {
     
            if (index < 0 || index > size - 1)
                throw new RuntimeException("索引错误" + index);
            int i = 0;
            pos = head;
            while (pos.next != head && i < index) {
                pos = pos.next;
                i++;
            }
            return pos.next.data;
        }
     
        /**
         * 查询元素索引
         * @param data
         * @return
         */
        public int indexOf(Object data) {
     
            int i = 0;
            pos = head;
            while (pos.next != head) {
                pos = pos.next;
                if (pos.data.equals(data))
                    return i;
                i++;
            }
            return -1;
        }
     
        /**
         * 判断是否包含指定元素
         */
        public void find(Object data) {
            pos = head;
            while (pos.next != head) {
                pos = pos.next;
                if (pos.data.equals(data))
                    System.out.println("含有该元素");
            }
            System.out.println("没有这个元素");
        }
     
        /**
         * 打印函数
         */
        public void print() {
            for (int i = 0; i < size; i++) {
                System.out.print(this.get(i) + "-->");
            }
            System.out.println();
        }
        public static void main(String[] args) {
        	CycleLinkedList list = new CycleLinkedList();
    		// 插入结点
    		list.add(10000);
    		list.add(10010);
    		list.add(10086);
    		list.add(12580);
    		list.add(12306);
    		System.out.println("初始链表:");
    		list.print();
    		list.insert(3, 12316);
    		System.out.println("插入链表:");
    		list.print();
    		list.remove(3);
    		System.out.println("删除指定索引链表:");
    		list.print();
    		System.out.println("查询链表:");
    		list.find(12110);
    	}
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值