Chapter 1 简介
Chapter 2 数组
三种初始化方法:
1.int[] a = new int[100];
2.int[] a = new int[]{1, 2, 3};
3.int[] a = {1, 2, 3};
动态数组
private void resize(int newCapacity) {
E[] newData = (E[])new Object[newCapacity];
for (int i = 0; i < size; i++)
newData[i] = data[i];
data = newData;//指向重定义尺寸的数组
}
时间复杂度分析
均摊情况下,resize的复杂度为O(1),addLast时间复杂度O(1),removeLast时间复杂度O(1)
复杂度震荡
解决:Lazy
if (size == data.length / 4 && data.length / 2 != 0)
resize(data.length / 2);
return result;
Chapter 3 栈和队列
栈
线性结构,只能在栈顶进行添加和删除——Last In First Out
应用:Undo、程序调用的系统栈
栈的实现
interface Stack<E>
void push(E)
E pop()
E peek()
int getSize()
boolean isEmpty()
括号匹配
栈顶元素反应了在嵌套的层次关系中,最近的需要匹配的元素
public boolean isValid(String s){
Stack<Character> stack = new Stack<>();
for (int i = 0; i < s.length(); i ++){
char c = s.charAt(i);
if (c == '(' || c == '[' || c == '{')
stack.push(c);
else {
if (stack.isEmpty())
return false;
char topChar = stack.pop();
if (c == ')' && topChar != '(')
return false;
if (c == ']' && topChar != '[')
return false;
if (c == '}' && topChar != '{')
return false;
}
}
return stack.isEmpty();
}
队列
线性结构,只能在队尾添加元素,在队首取出元素——First In First Out
队列的实现
interface Queue<E>
void enqueue(E)
E dequeue()
E getFront()
int getSize()
boolean isEmpty()
循环队列
front指向队首,tail指向队尾元素后一个位置
front == tail, 队列为空;
(tail + 1) % c == front, 队列满
注:capacity中,浪费一个空间
public class LoopQueue<E> implements Queue<E> {
private E[] data;
private int front, tail;
private int size;
public LoopQueue(int capacity){
data = (E[])new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue(){
this(10);
}
public int getCapacity(){
return data.length - 1;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return front == tail;
}
private void resize(int newCapacity){
E[] newData = (E[])new Object[newCapacity + 1];
for (int i = 0; i < size; i++)
newData[i] = data[(front + i) % data.length];
data = newData;
front = 0;
tail = size;
}
@Override
public void enqueue(E e) {
if ((tail + 1) % data.length == front)
resize(getCapacity() * 2);//不用length,因为天然浪费一个位置
data[tail] = e;
tail = (tail + 1) % data.length;
size ++;
}
@Override
public E dequeue() {
if (isEmpty())
throw new IllegalArgumentException("cannot dequeue from an empty queue!");
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size --;
if (size == getCapacity() / 4 && getCapacity() / 2 != 0)
resize(getCapacity() / 2);
return ret;
}
@Override
public E getFront() {
if (isEmpty())
throw new IllegalArgumentException("queue is empty!");
return data[front];
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append(String.format("Queue: size = %d, capacity = %d\n", size, getCapacity()));
res.append("front [");
for (int i = front; i != tail; i = (i + 1) % data.length){
res.append(data[i]);
if ((i + 1) % data.length != tail)
res.append(", ");
}
res.append("] tail");
return res.toString();
}
}
数组队列和循环队列的比较
Chapter 4 链表
线性数据结构
优点:真正动态,不需要处理固定容量的问题
缺点:丧失了随机访问的能力
链表的节点
private class Node{
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e, null);
}
public Node(){
this(null, null);
}
@Override
public String toString(){
return e.toString();
}
}
为链表添加元素
//在链表中间index(0-based)位置添加元素e
public void add(int index, E e){
if (index < 0 || index > size)
throw new IllegalArgumentException("add failed, illegal index");
Node prev = dummyHead;
for (int i = 0; i < index; i++)
prev = prev.next;
// Node node = new Node(e);
// node.next = prev.next;
// prev.next = node;
prev.next = new Node(e, prev.next);
size ++;
}
//在链表头添加元素e
public void addFirst(E e){
// Node node = new Node(e);
// node.next = head;
// head = node;
add(0, e);
}
//在链表末尾添加元素e
public void addLast(E e){
add(size, e);
}
注:顺序不能颠倒
为链表设立虚拟头结点
从链表中删除元素
//从链表中删除index(0-based)位置的元素,返回删除的元素
public E remove(int index){
if (index < 0 || index > size)
throw new IllegalArgumentException("remove failed, illegal index");
Node prev = dummyHead;
for (int i = 0; i < index; i++){
prev = prev.next;
}
Node retNode = prev.next;
prev.next = retNode.next;
retNode.next = null;
size --;
return retNode.e;
}
public E removeFirst(){
return remove(0);
}
public E removeLast(){
return remove(size - 1);
}
其他操作
//获得链表第index(0-based)个位置的元素
public E get(int index){
if (index < 0 || index > size)
throw new IllegalArgumentException("add failed, illegal index");
Node cur = dummyHead.next;
for (int i = 0; i < index; i++)
cur = cur.next;
return cur.e;
}
//获得链表第一个元素
public E getFirst(){
return get(0);
}
//获得链表最后一个元素
public E getLast(){
return get(size - 1);
}
//修改链表第index(0-based)个位置的元素为e
public void set(int index, E e){
if (index < 0 || index > size)
throw new IllegalArgumentException("set failed, illegal index");
Node cur = dummyHead.next;
for (int i = 0; i < index; i++)
cur = cur.next;
cur.e = e;
}
//查找链表中是否有元素e
public boolean contains(E e){
Node cur = dummyHead.next;
while (cur != null){
if (cur.e.equals(e))
return true;
cur = cur.next;
}
return false;
}
时间复杂度分析
增删改查:O(n)
使用链表实现栈
将链表头做为栈顶
public class LinkedListStack<E> implements Stack<E> {
private LinkedList<E> list;
public LinkedListStack(){
list = new LinkedList<E>();
}
@Override
public int getSize() {
return list.getSize();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public E pop() {
return list.removeFirst();
}
@Override
public void push(E e) {
list.addFirst(e);
}
@Override
public E peek() {
return list.getFirst();
}
}
带有尾指针的链表:使用链表实现队列
注:由于没有dummyHead,要注意链表为空的情况
public class LinkedListQueue<E> implements Queue<E> {
private class Node{
public E e;
public Node next;
public Node(E e, Node next){
this.e = e;
this.next = next;
}
public Node(E e){
this(e, null);
}
public Node(){
this(null, null);
}
@Override
public String toString(){
return e.toString();
}
}
private Node head, tail;
private int size;
public LinkedListQueue(){
head = null;
tail = null;
size = 0;
}
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public void enqueue(E e) {
if (tail == null){
tail = new Node(e);
head = tail;
}else {
tail.next = new Node(e);
tail = tail.next;
}
size ++;
}
@Override
public E dequeue() {
if (isEmpty())
throw new IllegalArgumentException("cannot dequeue from a empty queue");
Node retNode = head;
head = head.next;
retNode.next = null;
if (head == null)//队列中只有一个元素的情况
tail = null;
size --;
return retNode.e;
}
@Override
public E getFront() {
if (isEmpty())
throw new IllegalArgumentException("queue is empty");
return head.e;
}
@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append("Queue: front");
Node cur = head;
while (cur != null){
res.append(cur + "->");
cur = cur.next;
}
res.append("null tail");
return res.toString();
}
}
Chapter 5 链表与递归
删除单链表中所有val元素
解法一
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode removeElements(ListNode head, int val){
while (head != null && head.val == val){
//head = head.next;
ListNode delNode = head;
head = head.next;
delNode.next = null;
}
if (head == null)
return null;
ListNode prev = head;
while (prev.next != null){
if (prev.next.val == val){
//prev.next = prev.next.next;
ListNode delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
}else {
prev = prev.next;
}
}
return head;
}
}
解法二——使用虚拟头结点
public class Solution2 {
public ListNode removeElements(ListNode head, int val){
//创建虚拟头结点
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode prev = dummyHead;
while (prev.next != null){
if (prev.next.val == val){
//prev.next = prev.next.next;
ListNode delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
} else {
prev = prev.next;
}
}
return dummyHead.next;
}
}
链表和递归
本质:将原来的问题,转化为更小的同一问题
例:
public class Sum {
public static int sum(int[] arr){
return sum(arr, 0);
}
private static int sum(int[] arr, int l){
//求解基本问题
if (l == arr.length)
return 0;
//把原问题转化成更小的问题
return arr[l] + sum(arr, l + 1);
}
public static void main(String[] args) {
int[] nums = {
1, 2, 3, 4, 5, 6, 7, 8};
System.out.println(sum(nums));
}
}
解法三——删除链表中所有val
public class Solution3 {
public ListNode removeElements(ListNode head, int val){
if (head == null)
return null;
//res是head后跟的链表删除val后的结果
head.next = removeElements(head.next, val);
// if (head.val == val){
// //head需要删除,返回head后跟的链表删除val后的结果
// return head.next;
// }else {
// //head不需要删除
// return head;
// }
return head.val == val ? head.next : head;
}
递归运行机制
递归调用是有代价的:函数调用+系统栈空间
递归算法的调试
更多问题
双链表
class Node{
E e;
Node next, prev;
}
循环链表
class Node{
E e;
Node next, prev;
}
数组链表
class Node{
E e;
int next;
}
Chapter 6 二分搜索树
二叉树
动态数据结构、具有天然递归结构(左子树,右子树)
class Node {
E e;
Node left;
Node right;
}
二叉树是递归定义的,逻辑上二叉树有5中基本形态:
·空二叉树
·只有一个根节点的二叉树
·只有左子树
·只有右子树
·完全二叉树
满二叉树、完全二叉树、…
二分搜索树(Binary Search Tree)
·二分搜索树是二叉树
·二分搜索树的每个节点的值:
·大于其左子树所有节点的值
·小于其右子树所有节点的值
·二分搜索树的每一棵子树也是二分搜索树
注:存储的元素必须有可比较性: BST<E extends Comparable>
public class BST<E extends Comparable<E>> {
private class Node{
public E e;
public Node left, right;
public Node(E e){
this.e = e;
left = null;
right = null;
}
}
private Node root;
private int size;
public BST(){
root = null;
size = 0;
}
public int size(){
return size;
}
public boolean isEmpty(){
return size == 0;
}
}
1.BST添加新元素
//向BST添加新元素e
public void add(E e){
if (root == null){
root = new Node(e);
size ++;
}else {
add(root, e);
}
}
//向以node为根的BST插入元素e,递归算法
private void add(Node node, E e){
//递归终止条件
if (e.equals(node.e))