链表 LinkedList是双链表
链表是以节点的方式来存储, 是链式存储
每个节点包含 data 域, next 域:指向下一个节点.
如图:发现 链表的各个节点不一定是连续存储.
链表分 带头节点的链表和 没有头节点的链表,根据实际的需求来确定
单链表 就是单向的,有next–指向下一个节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 package 数据结构;public class SingleLinkedList { public static void main (String[] args) { Node Node1 = new Node (1 ); Node Node2 = new Node (2 ); LinkedList linkedList = new LinkedList (); linkedList.addFirstNode(Node1); linkedList.add(Node2); System.out.println(linkedList.getValue(2 ).toString()); } static class LinkedList { private Node head = new Node (0 ,null ); public void add (Node Node) { Node temp = head; while (temp.next!=null ){ temp = temp.next; } temp.next = Node; } public void addFirstNode (Node Node) { Node node = head; node.next = Node; } public void delNode (Node node) { node = node.next; node.next = node.next.next; } public Integer getValue (int index) { Node node = head; int i = 0 ; while (node.next!=null ){ i++; if (i==index){ return node.no; } node = node.next; } return null ; } } static class Node { public int no; public Node next; public Node (int No) { this .no = No; } public Node (int No,Node next) { this .no = No; this .next = next; } } }
力扣反转链表 赋值符号是右边赋值给左边,超时的可能原因还有:方法体内需要的参数在体外赋值可能会超时
这题注意参数的调用和读写,每个参数都有独特的意义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution { public ListNode reverseList (ListNode head) { ListNode newHead = null ; while (head != null ) { ListNode temp = head.next; head.next = newHead; newHead = head; head = temp; } return newhead; } }
顺序表和链表的优缺点 一、顺序表
顺序表的内存空间连续。
尾插、尾删效率较高,时间复杂度是O(1)。
支持随机访问,可以高效的按下标进行操作,时间复杂度是O(1)。
缺点:
在顺序表中间插入或删除元素时都涉及到元素的移动,效率较低,时间复杂度为O(N)。
顺序表长度固定,有时需要扩容。
二、链表:
优点
链表的内存 空间不连续。
如果知道要处理节点的前一个位置,则进行插入和删除的复杂度为O(1);
如果不知道要处理节点的前一个位置,则进行插入和删除的复杂度为O(N)。
头插、头删的效率高,时间复杂度 是O(1)。
没有空间限制,不会溢出,可以存储很多元素。
缺点:
链表不支持随机访问,查找元素效率低,需要遍历节点,时间复杂度是O(1)。
三、总结
当线性表的长度变化不大、易于确定其大小时,采用顺序表作为存储结构。
若线性表主要操作是查找。很少进行插入或删除操作时,采用顺序表作为存储结构。
对于频繁进行插入和删除的线性表,则应该使用链表作为存储结构。
队列(queue) 队列:有序列表,可以用数组或链表实现,先入先出
以下为循环队列:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 package queue;import java.util.Arrays;public class MyQueue { private Integer[] queue; private int size = 0 ; private int front = 0 ; private int rear = 0 ; public MyQueue (int length) { queue = new Integer [length + 1 ]; } public void enQueue (int value) { if (isFull()) { throw new RuntimeException ("Queue is full." ); } queue[rear] = value; rear = (rear + 1 ) % queue.length; size++; } public Integer outQueue () { Integer res = queue[front]; front = (front + 1 ) % queue.length; size--; return res; } public int Front () { if (isEmpty()){ return -1 ; }else { return queue[front]; } } public int Rear () { if (isEmpty()){ return -1 ; }else { return queue[(rear - 1 + queue.length) % queue.length]; } } public void clear () { size = 0 ; front = 0 ; rear = 0 ; Arrays.fill(queue, null ); } public boolean isNull () { return front = = rear; } public boolean isFull () { return (rear + 1 ) % queue.length == front; } @Override public String toString () { if (isNull()) { return "null" ; } int i = front; String str = "" ; while (i != rear) { str += queue[i] + "," ; i = (i + 1 ) % queue.length; } str = str.substring(0 , str.length() - 1 ); return "MyQueueSequence{" + str + "}" ; } } Queue<Integer> q = new LinkedList (); System.out.println("The first element is: " + q.peek()); q.offer(5 ); q.offer(13 ); q.offer(8 ); q.offer(6 ); q.poll(); System.out.println("The first element is: " + q.peek()); System.out.println("The size is: " + q.size());
队列的真性和假性溢出 真溢出:Q.rear==Q.front
假溢出:当队列中的存储空间没满时,但是由于来的元素堵在队尾,此时如果还有元素要入队的话,就会报错,发生溢出
为了解决这个问题,有如下方法:
修改出队操作算法,使每次出队后都把队列中剩余的操作元素向队头移动一个位置;
修改入队算法,增加判断条件,当发生“假性溢出”时,把数据元素向队头移动;
采用循环队列;
栈(stack) 定义:先进后出,后进先出,限定仅在表尾进行插入和删除操作的线性表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package it01;public class stack { public static void main (String[] args) { ArrayStack stack = new ArrayStack (4 ); stack.push(1 ); stack.push(2 ); stack.push(3 ); stack.push(4 ); stack.list(); } } class ArrayStack { private int maxSize; private int [] stack; private int top = -1 ; public ArrayStack (int maxSize) { this .maxSize = maxSize; stack = new int [this .maxSize]; } public boolean isFull () { return top = = (maxSize-1 ); } public boolean isEmpty () { return top = = -1 ; } public void push (int value) { if (isFull()){ System.out.println("栈已经满了,无法入栈" ); return ; } top++; stack[top] = value; } public int pop () { if (isEmpty()){ throw new RuntimeException ("栈空,没有数据" ); } return stack[top--]; } public void list () { if (isEmpty()) { System.out.println("栈空,没有数据" ); return ; } int i = top; while (i >= 0 ){ System.out.println(stack[i--]); } }
栈溢出 栈溢出的几种情况:
局部数组过大。当函数内部的数组过大时,有可能导致堆栈溢出。
递归调用层次太多。递归函数在运行时会执行压栈操作,当压栈次数太多时,也会导致堆栈溢出。
指针或数组越界。这种情况最常见,例如进行字符串拷贝,或处理用户输入等等。
栈的上溢与下溢 由于栈区域是在堆栈定义时就确定了的,因而栈工作过程中有可能产生溢出。栈溢出有两种情况可能发生:如栈已满,但还想再存入信息,这种情况称为栈上溢;另一种情况是,如栈已空,但还想再取出信息,这种情况称为栈下溢。
不论上溢或下溢,都是不允许的。因此在编制程序时,如果可能发生堆栈溢出,则应在程序中采取保护措施。这可以通过给SP规定上、下限,在进栈或出栈操作前先做SP和边界值的比较,如溢出则作溢出处理,以避免破坏其他存储区或使程序出错的情况发生。