二、一些基本的数据结构及操作
1、链表数据结构
package list;
public class Node<T> {
public T value;
public Node<T> next;
public Node(T value) {
this.value = value;
this.next = null;
}
}
2、对一个链表进行翻转
- 思路:新建立一个链表,对原链表依次遍历每个位置采用头插法插入到新链表的头部,最后返回新链表的头,采用三个变量。
- pre :新链表的头结点
- head:原链表的头结点
- next:原链表下一个待翻转节点
package list;
public class ReverseList {
public static <T> Node<T> reverseList(Node<T> head){
if(head == null || head.next == null){
return head;
}
Node<T> pre = null;
Node<T> next = null;
while (head != null){
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
}
3、给定一个值,删除链表中与该值相同的所有节点
package list;
public class DeleteElement {
public static <T> Node<T> deleteValue(Node<T> head, T value) {
if (head == null) {
return head;
}
while (head != null) {
if (!head.value.equals(value)) {
break;
}
head = head.next;
}
Node<T> pre = head;
Node<T> cur = head;
while (cur != null) {
if (cur.value.equals(value)) {
pre.next = cur.next;
} else {
pre = cur;
}
cur = cur.next;
}
return head;
}
}
4、用数组实现栈
- 栈:先进后出,加入和弹出都是在栈顶
- push、pop、peek都是常数操作
package stack;
public class ArrayToStack<V> {
private V[] data;
private int size;
public ArrayToStack(int size) {
data = (V[]) new Object[size];
this.size = 0;
}
public boolean isEmpty(){
return size == 0;
}
public boolean isFull(){
return data.length == size;
}
public int size(){
return size;
}
public boolean push(V value){
if(!isFull()){
data[size++] = value;
return true;
}
return false;
}
public V pop() throws Exception {
if(isEmpty()){
throw new Exception("当前栈为空......");
}
return data[--size];
}
public V peek() throws Exception {
if(isEmpty()){
throw new Exception("当前栈为空......");
}
return data[size - 1];
}
}
5、用已有栈结构实现pop、push、getMin都是常数的操作
- getMIn为弹出当前栈的最小元素
- 思路:准备两个栈,一个正常栈,一个最小栈。每次待加入元素若比最小栈顶的元素小则两个栈都加入该元素,若待加入元素大与最小栈顶元素则最小栈仍然加入它的栈顶元素,这样保证最小栈永远为当前栈里的最小元素。
package stack;
import java.util.Stack;
public class MinStack{
private Stack<Integer> stack;
private Stack<Integer> minStack;
public MinStack() {
this.stack = new Stack<>();
this.minStack = new Stack<>();
}
public Integer getMin() throws Exception {
if(minStack.isEmpty()){
throw new Exception("当前栈为空......");
}
return minStack.peek();
}
public Integer pop(){
minStack.pop();
return stack.pop();
}
public void push(Integer value) throws Exception {
if(stack.isEmpty()){
minStack.push(value);
}else if(value < getMin()){
minStack.push(value);
}else {
minStack.push(minStack.peek());
}
stack.push(value);
}
}
6、用数组实现队列
- 循环利用数组实现队列
- 加入两个辅助遍历pushIndex和popIndex记录下次出队和入队的位置,这样不用每次都进行判断
package queue;
public class ArrayToQueue<V> {
private V[] data;
private int size;
private int limit;
private int pushIndex;
private int pollIndex;
public ArrayToQueue(int limit) {
this.limit = limit;
this.size = 0;
data = (V[]) new Object[limit];
pollIndex = 0;
pushIndex = 0;
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == limit;
}
public void push(V value) throws Exception {
if (isFull()) {
throw new Exception("当前队列已满......");
}
size++;
data[pushIndex] = value;
pushIndex = (pushIndex + 1) % limit;
}
public V poll() throws Exception {
if (isEmpty()) {
throw new Exception("当前队列为空......");
}
size--;
V ans = data[pollIndex];
pollIndex = (pollIndex + 1) % limit;
return ans;
}
}
7、用队列实现栈
- 队列先进先出,栈先进后出
- 思路:两个队列。一个queue,一个help。每次元素进入help队列,这时候把queue队列的元素依次插入help队列尾部,在互换queue和help的引用。
package stackandqueue;
import java.util.LinkedList;
import java.util.Queue;
public class QueueToStack <V>{
private Queue<V> queue;
private Queue<V> help;
public QueueToStack() {
queue = new LinkedList<>();
help = new LinkedList<>();
}
public void push(V value){
help.offer(value);
while (!queue.isEmpty()){
help.offer(queue.poll());
}
Queue<V> tmp = queue;
queue = help;
help = tmp;
}
public V poll(){
return queue.poll();
}
}
- 上面做法是在每次加入时进行元素转换,下面还可以用在弹出时进行元素移动
package stackandqueue;
import java.util.LinkedList;
import java.util.Queue;
public class QueueToStackChange {
public class QueueToStack<V> {
private Queue<V> queue;
private Queue<V> help;
public QueueToStack() {
queue = new LinkedList<>();
help = new LinkedList<>();
}
public void push(V value) {
queue.offer(value);
}
public V poll() {
while (queue.size() > 1) {
help.offer(queue.poll());
}
V ans = queue.poll();
Queue<V> tmp = queue;
queue = help;
help = tmp;
return ans;
}
}
}
8、用栈实现队列
- 栈:先进后出
- 队列:先进先出
- 思路:用两个栈,一个push栈,一个pop栈。需要加入时元素从push加入,弹出时检查pop栈是否为空,不空直接返回pop栈顶元素,空的话需要将push栈的元素弹到pop中再从pop中弹出,感兴趣可以自己画图理解,这里不再画图。
package stackandqueue;
import java.util.Stack;
public class StackToQueue<V> {
private Stack<V> pushStack;
private Stack<V> popStack;
public StackToQueue() {
popStack = new Stack<>();
pushStack = new Stack<>();
}
public boolean isEmpty(){
return pushStack.isEmpty() && popStack.isEmpty();
}
public void push(V value){
pushStack.push(value);
}
public V poll() throws Exception {
if(isEmpty()){
throw new Exception("当前队列为空......");
}
if(popStack.isEmpty()){
while (!pushStack.isEmpty()){
popStack.push(pushStack.pop());
}
}
return popStack.pop();
}
}
9、Master公式(计算递归时间复杂度)
- 对于类似 T(N) = aT(N/b) + O(Nd) 的递推形式而言(即子问题规模一致)
- log(ba) < d ,O(Nd)
- log(ba) > d ,O(Nlog(ba))
- log(ba) = d ,O(NdlogN)
10、哈希表和有序表
10.1 哈希表
- 增删改查都是O(1)操作
- 哈希表的基础类型:String、基础类型及其包装类在Hash表下按值传递
10.2 有序表
- 在Java中为一个接口:TreeMap(用红黑树实现)
- 红黑树、avl、sb树、跳表N
- CRUD时间复杂度O(logN)
- 自定义类型需要重写排序算法