数据结构面试知识点总结3
#来自ウルトラマンティガ(迪迦)
1 线性表
最基本、最简单、最常用的一种数据结构。一个线性表是 n 个具有相同特性的数据元素的有限序列。
特征:数据元素之间是一对一的逻辑关系。
- 第一个数据元素没有前驱,称为头结点;
- 最后一个数据元素没有后继,称为尾结点;
- 除了头结点和尾结点,其他数据元素有且仅有一个前驱和一个后继。
分类:
- 顺序存储
- 链式存储
2 顺序表
顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元,依次存储线性表中的各个元素。
public class SequenceList<T> implements Iterable<T> { //存储元素的数组 private T[] eles; //记录当前顺序表中的元素个数 private int N; //构造方法 public SequenceList(int capacity) { //初始化数组 this.eles = (T[]) new Object[capacity]; //初始化长度 this.N = 0; } //将一个线性表置为空表 public void clear() { this.N = 0; } //判断当前线性表是否为空表 public boolean isEmpty() { return N == 0; } //获取线性表的长度 public int length() { return N; } //获取指定位置的元素 public T get(int i) { return eles[i]; } //向线型表中添加元素 t public void insert(T t) { if (N == eles.length) { resize(2 * eles.length); } eles[N++] = t; } //在 i 元素处插入元素 t public void insert(int i, T t) { if (N == eles.length) { resize(2 * eles.length); } //先把i索引处的元素及其后面的元素依次向后移动一位 for (int index = N; index > i; index--) { eles[index] = eles[index - 1]; } //再把t元素放到i索引处即可 eles[i] = t; //元素个数+1 N++; } //删除指定位置i处的元素,并返回该元素 public T remove(int i) { //记录索引i处的值 T current = eles[i]; //索引i后面元素依次向前移动一位即可 for (int index = i; index < N - 1; index++) { eles[index] = eles[index + 1]; } //元素个数-1 N--; if (N < eles.length / 4) { resize(eles.length / 2); } return current; } //查找t元素第一次出现的位置 public int indexOf(T t) { for (int i = 0; i < N; i++) { if (eles[i].equals(t)) { return i; } } return -1; } //根据参数newSize,重置eles的大小 public void resize(int newSize) { //定义一个临时数组,指向原数组 T[] temp = eles; //创建新数组 eles = (T[]) new Object[newSize]; //把原数组的数据拷贝到新数组即可 for (int i = 0; i < N; i++) { eles[i] = temp[i]; } } @Override public Iterator<T> iterator() { return new SIterator(); } private class SIterator implements Iterator { private int cursor; public SIterator() { this.cursor = 0; } @Override public boolean hasNext() { return cursor < N; } @Override public T next() { return eles[cursor++]; } } }
复杂度:
get(i):时间复杂度 O(1)
insert(int i, T t):时间复杂度 O(n)
remove(int i):时间复杂度 O(n)
3 链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列的结点(链表中的每一个元素称为结点)组成,结点可以在运行时动态生成。
//链表结点 public class Node<T> { //存储元素 public T item; //指向下一个结点 public Node next; public Node(T item, Node next) { this.item = item; this.next = next; } }
复杂度:
get(i):时间复杂度 O(n)
insert(int i, T t):时间复杂度 O(1)
remove(int i):时间复杂度 O(1)
3.1 单向链表
单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据,指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。
public class LinkList<T> implements Iterable<T> { //头结点 private Node head; //链表的长度 private int N; //结点类 private class Node { //存储数据 T item; //下一个结点 Node next; public Node(T item, Node next) { this.item = item; this.next = next; } } public LinkList() { //初始化头结点、 this.head = new Node(null, null); //初始化元素个数 this.N = 0; } //清空链表 public void clear() { head.next = null; this.N = 0; } //获取链表的长度 public int length() { return N; } //判断链表是否为空 public boolean isEmpty() { return N == 0; } //获取指定位置i出的元素 public T get(int i) { //通过循环,从头结点开始往后找,依次找i次,就可以找到对应的元素 Node n = head.next; for (int index = 0; index < i; index++) { n = n.next; } return n.item; } //向链表中添加元素t public void insert(T t) { //找到当前最后一个结点 Node n = head; while (n.next != null) { n = n.next; } //创建新结点,保存元素t Node newNode = new Node(t, null); //让当前最后一个结点指向新结点 n.next = newNode; //元素的个数+1 N++; } //向指定位置i出,添加元素t public void insert(int i, T t) { //找到i位置前一个结点 Node pre = head; for (int index = 0; index <= i - 1; index++) { pre = pre.next; } //找到i位置的结点 Node curr = pre.next; //创建新结点,并且新结点需要指向原来i位置的结点 Node newNode = new Node(t, curr); //原来i位置的前一个节点指向新结点即可 pre.next = newNode; //元素的个数+1 N++; } //删除指定位置i处的元素,并返回被删除的元素 public T remove(int i) { //找到i位置的前一个节点 Node pre = head; for (int index = 0; index <= i - 1; i++) { pre = pre.next; } //要找到i位置的结点 Node curr = pre.next; //找到i位置的下一个结点 Node nextNode = curr.next; //前一个结点指向下一个结点 pre.next = nextNode; //元素个数-1 N--; return curr.item; } //查找元素t在链表中第一次出现的位置 public int indexOf(T t) { //从头结点开始,依次找到每一个结点,取出item,和t比较,如果相同,就找到了 Node n = head; for (int i = 0; n.next != null; i++) { n = n.next; if (n.item.equals(t)) { return i; } } return -1; } @Override public Iterator<T> iterator() { return new LIterator(); } private class LIterator implements Iterator { private Node n; public LIterator() { this.n = head; } @Override public boolean hasNext() { return n.next != null; } @Override public Object next() { n = n.next; return n.item; } } //用来反转整个链表 public void reverse() { //判断当前链表是否为空链表,如果是空链表,则结束运行,如果不是,则调用重载的reverse方法完成反转 if (isEmpty()) { return; } reverse(head.next); } //反转指定的结点curr,并把反转后的结点返回 public Node reverse(Node curr) { if (curr.next == null) { head.next = curr; return curr; } //递归的反转当前结点curr的下一个结点;返回值就是链表反转后,当前结点的上一个结点 Node pre = reverse(curr.next); //让返回的结点的下一个结点变为当前结点curr; pre.next = curr; //把当前结点的下一个结点变为null curr.next = null; return curr; } }
3.2 双向链表
双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域用来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结点。
public class TowWayLinkList<T> implements Iterable<T> { //首结点 private Node head; //最后一个结点 private Node last; //链表的长度 private int N; //结点类 private class Node { //存储数据 T item; //指向上一个结点 Node pre; //指向下一个结点 Node next; public Node(T item, Node pre, Node next) { this.item = item; this.pre = pre; this.next = next; } } public TowWayLinkList() { //初始化头结点和尾结点 this.head = new Node(null, null, null); this.last = null; //初始化元素个数 this.N = 0; } //清空链表 public void clear() { this.head.next = null; this.head.pre = null; this.head.item = null; this.last = null; this.N = 0; } //获取链表长度 public int length() { return N; } //判断链表是否为空 public boolean isEmpty() { return N == 0; } //获取第一个元素 public T getFirst() { if (isEmpty()) { return null; } return head.next.item; } //获取最后一个元素 public T getLast() { if (isEmpty()) { return null; } return last.item; } //插入元素t public void insert(T t) { if (isEmpty()) { //如果链表为空: //创建新的结点 Node newNode = new Node(t, head, null); //让新结点称为尾结点 last = newNode; //让头结点指向尾结点 head.next = last; } else { //如果链表不为空 Node oldLast = last; //创建新的结点 Node newNode = new Node(t, oldLast, null); //让当前的尾结点指向新结点 oldLast.next = newNode; //让新结点称为尾结点 last = newNode; } //元素个数+1 N++; } //向指定位置i处插入元素t public void insert(int i, T t) { //找到i位置的前一个结点 Node pre = head; for (int index = 0; index < i; index++) { pre = pre.next; } //找到i位置的结点 Node curr = pre.next; //创建新结点 Node newNode = new Node(t, pre, curr); //让i位置的前一个结点的下一个结点变为新结点 pre.next = newNode; //让i位置的前一个结点变为新结点 curr.pre = newNode; //元素个数+1 N++; } //获取指定位置i处的元素 public T get(int i) { Node n = head.next; for (int index = 0; index < i; index++) { n = n.next; } return n.item; } //找到元素t在链表中第一次出现的位置 public int indexOf(T t) { Node n = head; for (int i = 0; n.next != null; i++) { n = n.next; if (n.next.equals(t)) { return i; } } return -1; } //删除位置i处的元素,并返回该元素 public T remove(int i) { //找到i位置的前一个结点 Node pre = head; for (int index = 0; index < i; index++) { pre = pre.next; } //找到i位置的结点 Node curr = pre.next; //找到i位置的下一个结点 Node nextNode = curr.next; //让i位置的前一个结点的下一个结点变为i位置的下一个结点 pre.next = nextNode; //让i位置的下一个结点的上一个结点变为i位置的前一个结点 nextNode.pre = pre; //元素的个数-1 N--; return curr.item; } @Override public Iterator<T> iterator() { return new TIterator(); } private class TIterator implements Iterator { private Node n; public TIterator() { this.n = head; } @Override public boolean hasNext() { return n.next != null; } @Override public Object next() { n = n.next; return n.item; } } }
3.3 循环链表
链表整体要形成一个圆环状。让单向链表的最后一个节点的指针指向头结点。
4 题目
4.1 反转链表
LeetCode:24 题
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
示例:
输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
class Solution { public ListNode reverseList(ListNode head) { ListNode pre = null; ListNode cur = head; while(cur != null) { ListNode tmp = cur.next; cur.next = pre; pre = cur; cur = tmp; } return pre; } }
4.2 快慢指针
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
示例:
输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。
public class Solution { public boolean hasCycle(ListNode head) { //快慢指针 if(head == null || head.next == null) return false; ListNode slow = head; ListNode fast = head.next; while(slow != fast){ if(fast == null || fast.next == null) return false; slow = slow.next; fast = fast.next.next; } return true; } }
原文地址:https://blog.csdn.net/2302_77186925/article/details/140540844
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!