链表
- 请说出集中基本的数据结构。
常见的基本数据结构有链表、栈、队列、树。
- 链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑结构是通过链表中的指针连接次序实现的。链表是由一系列节点这组成,这些节点不必在内存中相连,每个节点由数据部分Data和链部分Next组成,Next指向下一个节点,这样当添加或删除时,只需要改变相关节点的Next的指向,效率较高。【链表在内存上不连续,字逻辑上连续】
- 栈是限制插入和删除智能在一个位置上进行的表,【后进先出】
- 队列只允许在front端进行删除操作,在rear端进行插入操作。【先进先出】
- 树是一种非线性数据结构,主要考察二叉树。它是由n(n>=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;
}
}
- 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
思路:- 定义虚拟节点
- 定义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; } }
- 如何判断一个单向链表存在回路?(请问如何判断一个链表是否有环)
方法1:用一个指针数组A,存储已访问过的节点。用一个指针p,每次都在链表上移动一步,然后与指针数组A比较,若数组中没有指针与p相同,说明第一次访问p,将p放入数组中;若有指针与p相同,则存在环路,且第一次相同的节点为环的入口。(链表长度为n,则需要空间o(n),且每次要与指针数组比较,时间复杂度为 O(n^2)。)
方法2:用两个指针(fast,slow),慢指针一次跳一步,快指针一次条两步,如果块的能追上慢的就表示有环。 - 如何判断两个链表是否相交?
从头遍历两个链表。创建两个栈,第一个栈存储第一个链表的节点,第二个栈存储第二个链表的节点,每遍历到一个节点,就将该节点入栈。两个节点都入栈后,则通过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; } }
- 循环链表的增添,删除,插入,修改,查询
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); } }