算法与数据结构思维导图
参考左程云算法课程
题目链接
package class03;
public class LinkedList {
// 单链表节点的定义
class Node {
// 定义节点值和指针
public int value; // 支持泛型
public Node next; // 指向下个节点的指针
public Node(int value, Node next) {
this.value = value;
this.next = next;
}
public Node(int value) {
this(value, null);
}
public Node() {
this(0, null);
}
}
// 双链表节点的定义
class DoubleNode {
// 定义节点值和双指针
public int value;
public DoubleNode left; // 定义指向前一个节点的左指针
public DoubleNode right; // 定义指向后一个节点的右指针
public DoubleNode(int value, DoubleNode left, DoubleNode right) {
super();
this.value = value;
this.left = left;
this.right = right;
}
public DoubleNode(int value) {
this(value, null, null);
}
public DoubleNode() {
this(0, null, null);
}
}
}
package class03;
import class03.LinkedList.DoubleNode;
import class03.LinkedList.Node;
public class Code01_ReverseList {
// 单链表反转
public Node reverseList (Node head) {
Node left = null; // 指向左节点的指针
Node cur = head; // 当前要处理的节点指针
Node right = null;// 指向右节点的指针
while(cur != null) {
// 1. 反转指针指向
right = cur.next; // 先保留好右节点
cur.next = left; // 反转当前节点
// 2. 平移指针,进行下一个节点的反转
left = cur;
cur = right;
}
return left; // 返回最后一个节点,即头节点
}
// 双链表反转
public DoubleNode reverseDoubleList(DoubleNode head) {
DoubleNode left = null;
DoubleNode right = null;
DoubleNode cur = head;
while (cur != null) {
right = cur.right;
cur.right = left;
cur.left = right;
left = cur;
cur = right;
}
return left;
}
}
head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。package class03;
public class Code02_DeleteGivenValue {
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int x) {
val = x;
}
}
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(); // 定义虚拟头节点
dummyHead.next = head;
ListNode pre = dummyHead; // 寻找目标元素的指针
while(pre.next != null) { // 查找每个元素
if(pre.next.val == val) { // 找到了目标元素,开始处理
ListNode cur = pre.next; // 先将目标元素保存
pre.next = cur.next; // 删除目标元素
cur.next = null; // 将目标元素指向设空,方便垃圾回收
} else { // 如果连续的目标元素均删除完了,就继续找
pre = pre.next;
}
}
// 注意若head删除了,此时dummyHead的next指针已经改变了,
// 指向的是新的头节点
return dummyHead.next;
}
}
package class03;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;
public class Code03_DoubleEndsListToStackAndQueue {
public static class Node<E> {
public E value;
public Node<E> pre;
public Node<E> next;
public Node(E value) {
this.value = value;
}
}
// 自定义有头,尾指针的双向链表
public static class DoubleEndsList<E> {
private Node<E> head; // 指向头节点的指针
private Node<E> tail; // 指向尾节点的指针
// 从头部添加元素
public void addFirst(E value) {
// 1. 先根据节点value,创建节点对象
Node<E> cur = new Node<>(value);
// 2. 向空链表添加节点
if (head == null) {
head = cur;
tail = cur;
// 3. 向非空链表添加元素,注意维护head指针
} else {
// 先建立连接
cur.next = head;
head.pre = cur;
// 再改变头指针指向
head = cur;
}
}
// 从尾部添加元素
public void addlast(E value) {
Node<E> cur = new Node<>(value);
if (head == null) {
head = cur;
tail = cur;
} else {
tail.next = cur;
cur.pre = tail;
tail = cur;
}
}
// 删除头部元素,并返回删除元素节点的值
public E removeFirst() {
// 1. 先处理链表为空的情况
if (head == null)
return null;
// 2. 先保存好要删除的节点元素
Node<E> cur = head;
// 3. 维护头节点指针,将cur的next指向null,方便垃圾回收
// 注意链表中只有一个节点的情况
if (head == tail) {
head = null;
tail = null;
} else {
// 先改变指针指向
head = cur.next;
// 再断了连接
head.pre = null;
cur.next = null;
}
return cur.value;
}
// 删除尾部元素,并返回删除元素节点的值
public E removeLast() {
if (head == null)
return null;
Node<E> cur = tail;
if (head == tail) {
head = null;
tail = null;
} else {
// 先改变指针指向
tail = cur.pre;
// 再断了连接
tail.next = null;
cur.pre = null;
}
return cur.value;
}
public boolean isEmpty() {
return head == null;
}
}
// 用双端链表实现队列
public static class MyQueue<E> {
// 自定义的双端链表作为成员变量
private DoubleEndsList<E> list;
public MyQueue() {
list = new DoubleEndsList<>();
}
// 入队操作
public void push(E value) {
list.addFirst(value);
}
// 出队操作,先进先出,最先进的元素成了链表的尾部元素
public E poll() {
return list.removeLast();
}
public boolean isEmpty() {
return list.isEmpty();
}
}
// 用双端链表实现栈
public static class MyStack<E> {
// 自定义的双端链表作为成员变量
private DoubleEndsList<E> list;
public MyStack() {
list = new DoubleEndsList<>();
}
// 入栈操作
public void push(E value) {
list.addFirst(value);
}
// 出栈操作,后进先出,最后进的元素成了链表的头部元素
public E pop() {
return list.removeFirst();
}
public boolean isEmpty() {
return list.isEmpty();
}
}
// 重定义equals方法
public static boolean isEqual(Integer o1, Integer o2) {
if (o1 == null && o2 != null) {
return false;
}
if (o1 != null && o2 == null) {
return false;
}
if (o1 == null && o2 == null) {
return true;
}
return o1.equals(o2);
}
}
public static void main(String[] args) {
int oneTestDataNum = 100;
int value = 10000;
int testTimes = 100000;
for (int i = 0; i < testTimes; i++) {
MyStack<Integer> myStack = new MyStack<>();
MyQueue<Integer> myQueue = new MyQueue<>();
Stack<Integer> stack = new Stack<>();
Queue<Integer> queue = new LinkedList<Integer>();
for (int j = 0; j < oneTestDataNum; j++) {
int nums = (int) (Math.random() * value);
if (stack.isEmpty()) {
myStack.push(nums);
stack.push(nums);
} else {
if (Math.random() < 0.5) {
myStack.push(nums);
stack.push(nums);
} else {
if (!isEqual(myStack.pop(), stack.pop())) {
System.out.println("oops!");
}
}
}
int numq = (int) (Math.random() * value);
if (stack.isEmpty()) {
myQueue.push(numq);
queue.offer(numq);
} else {
if (Math.random() < 0.5) {
myQueue.push(numq);
queue.offer(numq);
} else {
if (!isEqual(myQueue.poll(), queue.poll())) {
System.out.println("oops!");
}
}
}
}
}
System.out.println("finish!");
}
finish!
package class03;
/**
* 测试用数组实现栈和队列
* @author ls
*
*/
public class Code04_ArraytoStackAndQueue {
/**
* 自定义可改变容量的动态数组,并实现基本增删改查功能
* @author ls
*
* @param 支持泛型
*/
public static class Array<E> {
// 定义成员变量:数组、大小
private E[] data;
private int size = 0;
// 定义构造函数,可以构建指定起始大小的数组
@SuppressWarnings("unchecked")
public Array(int capacity) {
data = (E[]) new Object[capacity]; // 泛型,用基类
}
public Array() {
this(10); // 默认起始容量10
}
// 1. 定义基本方法:获取数组基本信息
// 数组本身对象
public E[] getArray() {
return data;
}
// 数组中已有元素数量
public int getSize() {
return size;
}
// 数组容量
public int getCapacity() {
return data.length;
}
// 是否为空
public boolean isEmpty() {
return size == 0;
}
// 2. 查、找数组中指定索引的元素,时间O(1)
public E getValue(int index) {
checkIndex(index); // // 先判断边界条件
return data[index];
}
private void checkIndex(int index) {
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Get failed. Indext is illegal!");
}
}
public E getFirst() {
return getValue(0);
}
public E getLast() {
return getValue(size-1);
}
// 3. 改、将指定索引元素进行修改,时间O(1)
public void setValue(int index, E value) {
checkIndex(index);
data[index] = value;
}
// 4. 增、向指定索引位置添加元素,注意扩容操作
public void add(int index, E value) {
// 注意:每次增加元素前,先判断数组容量,防止边界溢出
if(size == data.length) {
resize(2 * data.length);
}
if(index < 0 || index > size) { // index == size可以添加
throw new IllegalArgumentException("Get failed. Indext is illegal!");
}
// 指定位置添加不覆盖,需要将其及后的元素后移挪位
for(int i = index; i < size; i ++) { // 容量大于size,i+1=size故不会溢出
data[i + 1] = data[i]; // 但遍历方法需i+1
}
data[index] = value;
size ++; // 切记,一定不要忘记维护size的大小
}
public void addFirst(E value) { // 时间O(n)
add(0, value);
}
public void addLast(E value) { // 均摊复杂度时间O(1)
add(size, value);
}
// 改变容量大小,时间O(n)
// 核心思想是重新开辟一个空间,将原有数组复制
private void resize(int newCapacity) {
@SuppressWarnings("unchecked")
// 1. 建新空间
E[] newData = (E[]) new Object[newCapacity];
// 2. 将旧数组元素copy到新数组中
for(int i = 0; i < size; i ++) {
newData[i] = data[i];
}
// 3. 用旧数组指针指向新数组对象
data = newData;
}
// 5. 删、
// 5.1 删除指定索引位置元素
public E remove(int index) {
checkIndex(index);
if(isEmpty()) {
throw new IllegalArgumentException("can't remove from empty of array!");
}
E res = data[index];
// 删除的逻辑是,将目标索引后面的元素依次往前移一位,覆盖
for(int i = index; i < size - 1; i ++) { // 边界处理
data[i] = data[i + 1];
}
size --; // 同样,不要忘记对size的维护
// 懒惰缩容,防止添加,删除频繁情况下的频繁动容量
if(size == data.length / 4 && data.length / 2 != 0) {
resize(data.length / 2);
}
return res;
}
public E removeFirst() { // O(n)
return remove(0);
}
public E removeLast() { // 均摊O(1)
return remove(size-1);
}
// 5.2 删除指定元素
public void removeElement(E value) {
int index = find(value);
if(index != -1) {
remove(index);
}
}
// 查找指定元素的索引,一般是辅助方法
public int find(E value) {
for(int i = 0; i < size; i ++) {
if(data[i].equals(value)) {
return i;
}
}
return -1;
}
public boolean contains(E e) {
for(int i = 0; i < size; i++) {
if(data[i].equals(e)) return true;
}
return false;
}
// 6. 为了能打印出数组,重写String方法
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append(String.format("Array: size = %d, capacity = %d\n",
size, data.length));
res.append("[");
for(int i = 0; i < size; i ++) {
res.append(data[i]);
if(i != size - 1) {
res.append(",");
}
}
res.append("]");
return res.toString();
}
}
/**
* 用自定义动态数组实现自定义的栈,实现栈接口的常用定义
* @author ls
*
* @param
*/
public static class ArrayStack<E> implements Stack<E>{
private Array<E> arr;
public ArrayStack() {
arr = new Array<>();
}
@Override
public int getSize() {
return arr.size;
}
@Override
public boolean isEmpty() {
return arr.isEmpty();
}
@Override
public void push(E e) {
arr.addLast(e);
}
@Override
public E pop() {
return arr.removeLast();
}
@Override
public E peek() {
return arr.getLast();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Stack:");
res.append("[");
for(int i = 0; i < arr.getSize(); i++) {
res.append(arr.getValue(i));
if(i != arr.getSize()-1) {
res.append(",");
}
}
res.append("] Top");
return res.toString();
}
}
public static class ArrayQueue<E> implements Queue<E> {
private Array<E> arr = new Array<>();
@Override
public void add(E e) {
arr.addLast(e);
}
@Override
public E poll() {
return arr.removeFirst();
}
@Override
public E peek() {
return arr.getFirst();
}
@Override
public int getSize() {
return arr.getSize();
}
@Override
public boolean isEmpty() {
return arr.isEmpty();
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("Queue:");
res.append("Front [");
for(int i = 0; i < arr.getSize(); i++) {
res.append(arr.getValue(i));
if(i != arr.getSize()-1) {
res.append(",");
}
}
res.append("] Tail");
return res.toString();
}
}
/**
* 用固定容量大小的循环数组实现队列
*/
public static class RingArrayQueue {
private int[] arr;
private int pushi;// tail
private int polli;// front
private int size;
private final int limit; // 数组容量大小
public RingArrayQueue(int limit) {
arr = new int[limit];
pushi = 0;
polli = 0;
size = 0;
this.limit = limit;
}
public void push(int value) {
if (size == limit) {
throw new RuntimeException("队列满了,不能再加了");
}
size++;
arr[pushi] = value;
pushi = nextIndex(pushi);
}
public int pop() {
if (size == 0) {
throw new RuntimeException("队列空了,不能再拿了");
}
size--;
int ans = arr[polli];
polli = nextIndex(polli);
return ans;
}
public boolean isEmpty() {
return size == 0;
}
// 如果现在的下标是i,返回下一个位置
private int nextIndex(int i) {
return i < limit - 1 ? i + 1 : 0;
}
}
}
public static void main(String[] args) {
// 测试数组
System.out.println("测试数组: ");
Array<Integer> arr = new Array<>();
for(int i = 0; i < 11; i ++) {
arr.addLast(i);
}
System.out.println(arr);
for(int i = 0; i < 6; i ++) {
arr.removeLast();
}
arr.removeElement(4);
System.out.println(arr);
// 测试栈
System.out.println("测试栈: ");
ArrayStack<Integer> stack = new ArrayStack<Integer>();
for(int i = 0; i < 11; i ++) {
stack.push(i);
}
System.out.println(stack);
System.out.println(stack.pop());
System.out.println(stack.peek());
System.out.println(stack);
// 测试队列
System.out.println("测试队列: ");
ArrayQueue<Integer> queue = new ArrayQueue<>();
for(int i = 0; i < 10; i ++) {
queue.add(i);
}
System.out.println(queue);
System.out.println(queue.poll());
System.out.println(queue);
}
测试数组:
Array: size = 11, capacity = 20
[0,1,2,3,4,5,6,7,8,9,10]
Array: size = 4, capacity = 10
[0,1,2,3]
测试栈:
Stack:[0,1,2,3,4,5,6,7,8,9,10] Top
10
9
Stack:[0,1,2,3,4,5,6,7,8,9] Top
测试队列:
Queue:Front [0,1,2,3,4,5,6,7,8,9] Tail
0
Queue:Front [1,2,3,4,5,6,7,8,9] Tail
package class03;
import java.util.Stack;
public class Code05_GetMinStack {
public static class MinStack {
private Stack<Integer> dataStack; // 装数据的栈,用以push,pop
private Stack<Integer> minStack; // 方便取最小值的栈
public MinStack() {
dataStack = new Stack<Integer>();
minStack = new Stack<Integer>();
}
// 添加操作,O(1)
public void push(int x) {
dataStack.push(x);
if(minStack.isEmpty()) { // 注意不能用minStack==null来判断空
minStack.push(x); // 对象不空,但值空也不行
} else {
int min = minStack.peek();
minStack.push(x < min ? x : min);
}
}
// 删除操作,O(1)
public void pop() {
if(dataStack.isEmpty()) {
throw new IllegalArgumentException("error!");
}
dataStack.pop();
minStack.pop();
}
public int top() {
return dataStack.peek();
}
public int min() {
return minStack.peek();
}
}
public static void main(String[] args) {
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
System.out.println(minStack.min()); //返回 -3.
minStack.pop();
System.out.println(minStack.top()); //返回 0.
System.out.println(minStack.min()); //返回 -2.
}
}
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
package class03;
import java.util.Stack;
public class Code06_TwoStacksImplementQueue {
public static class MyQueue {
private Stack<Integer> pushStack;
private Stack<Integer> popStack;
public MyQueue() {
pushStack = new Stack<>();
popStack = new Stack<>();
}
// 添加操作,是压入倒push栈,可以触发倒栈工作
public void push(int x) {
pushStack.push(x);
pushToPop();
}
// push向pop栈倒入数据,满足两个条件:
// 1. pop栈为空时才能倒,以防打乱原pop栈的出栈顺序
// 2. push栈一旦倒,就要倒完,以防打乱原push栈数的顺序
private void pushToPop() {
if(popStack.isEmpty()) { // 条件1
while(!pushStack.isEmpty()) { // 条件2
popStack.push(pushStack.pop());
}
}
}
// 删除操作,是从pop栈中弹出,也可触发倒栈工作
public int pop() {
if (pushStack.empty() && popStack.empty()) {
throw new RuntimeException("Queue is empty!");
}
pushToPop(); // 两个栈但凡有一个不为空,倒过后,popStack一定不空
return popStack.pop();
}
public int peek() {
if (pushStack.empty() && popStack.empty()) {
throw new RuntimeException("Queue is empty!");
}
pushToPop();
return popStack.peek();
}
public boolean empty() {
return pushStack.isEmpty() && popStack.isEmpty();
}
}
}
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
package class03;
import java.util.LinkedList;
import java.util.Queue;
public class Code07_TwoQueueImplementStack {
public static class MyStack {
private Queue<Integer> queue;
private Queue<Integer> help;
public MyStack() {
queue = new LinkedList<Integer>();
help = new LinkedList<Integer>();
}
// 添加、直接在数据queue中添加即可
public void push(int x) {
queue.add(x);
}
// 删除、要将数据导入help栈中,留下最后一个栈顶元素拿出
public int pop() {
if(queue.isEmpty()) return -1;
while(queue.size() > 1) { // 倒数据
help.add(queue.poll());
}
int res = queue.poll(); // 保留要拿出的数据
// 交换指针
Queue<Integer> temp = queue;
queue = help;
help = temp;
return res;
}
// 查询栈顶元素,于pop方法类似
public int top() {
if(queue.isEmpty()) return -1;
while(queue.size() > 1) {
help.add(queue.poll());
}
int res = queue.poll(); // 不是peek,倒数据后容器要清空
help.add(res); // 查看不删除,故查看后要放回队列中
// 交换指针
Queue<Integer> temp = queue;
queue = help;
help = temp;
return res;
}
public boolean empty() {
return queue.isEmpty();
}
}
public static void main(String[] args) {
MyStack stack = new MyStack();
stack.push(1);
stack.push(2);
System.out.println(stack.top());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.empty());
}
}
2
2
1
true
package class03;
/**
* 使用递归求一组数中的最大值
*
* @author ls
*
*/
public class Code08_GetMax {
// 求数组中的最大值
public static int getMax(int[] arr) {
return getMax(arr, 0, arr.length - 1);
}
private static int getMax(int[] arr, int L, int R) {
// 1. 基本问题
if(L >= R) return arr[L];
// 2. 处理当前层
int mid = L + ((R - L) >> 1);
// 3. 将较大问题转为较小问题
int leftMax = getMax(arr, L, mid);
int rightMax = getMax(arr, mid + 1, R);
// 4. 现场恢复,将较小问题组合成较大问题
return Math.max(leftMax, rightMax);
}
public static void main(String[] args) {
int[] arr = {2,4,5,8,3,9,10};
System.out.println(getMax(arr)); // 10
}
}