在日常刷题和面试过程中,链表问题还是占有一定的分量的,下面是一些简单常见的链表题目
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode next = null;
while(head != null){
next = head.next;
head.next = pre;
pre = head;
head = next;
}
return pre;
}
}
原理同单链表一致,只不过多了一个last【head.last = next】
//定义双链表
public static class DoubleNode{
public int val;
public DoubleNode last;
public DoubleNode next;
public DoubleNode(int val){
this.val = val;
}
}
public static DoubleNode reverseDoubleList(DoubleNode head){
DoubleNode next = null;
DoubleNode pre = null;
while(head != null){
next = head.next;
head.next = pre;
head.last = next;
pre = head;
head = next;
}
return pre;
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
//如果链表的头与要删除的元素值相同
while(head != null){
if(head.val != val){
break;
}
head = head.next;
}
//链表头的val与要删除的val不同
ListNode pre = head;
ListNode cur = head;
while(cur != null){
if(cur.val == val){
//值相等,就跳一个
pre.next = cur.next;
} else {
//值不等,直接等
pre = cur;
}
cur = cur.next;
}
return head;
}
}
栈:先进后出【弹夹】
队列:先进先出【水管】
核心:用两个栈,一个pop,一个push,定义两个栈转换的方法
//用栈实现队列
class MyQueue {
//push栈
public Stack<Integer> stackPush;
//pop栈
public Stack<Integer> stackPop;
public MyQueue() {
stackPush = new Stack<Integer>();
stackPop = new Stack<Integer>();
}
//push栈向pop栈倒数据
private void pushToPop(){
if(stackPop.empty()){
while(!stackPush.empty()){
stackPop.push(stackPush.pop());
}
}
}
public void push(int x) {
stackPush.push(x);
//数据进入栈后,马上转到pop栈
pushToPop();
}
public int pop() {
if(stackPop.empty() && stackPush.empty()){
throw new RuntimeException("queue is empty!");
}
//数据转移
pushToPop();
return stackPop.pop();
}
public int peek() {
if(stackPop.empty() && stackPush.empty()){
throw new RuntimeException("queue is empty!");
}
pushToPop();
return stackPop.peek();
}
public boolean empty() {
pushToPop();
return stackPop.isEmpty();
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
思路:主要是用两个队列,一个是做辅助的help,只要当涉及到pop、peek等操作的时候才会用到
//用队列实现栈
class MyStack {
public Queue<Integer> queue;
public Queue<Integer> help;
public MyStack() {
queue = new LinkedList<Integer>();
help = new LinkedList<Integer>();
}
public void push(int x) {
queue.offer(x);
}
//queue :5 4 3 2 1
//help:
public int pop() {
while(queue.size() > 1){
help.offer(queue.poll());
}
//queue: 5
//help: 4 3 2 1
int res = queue.poll();//要返回的最终结果
Queue<Integer> temp = queue;
queue = help;
help = temp;
//queue:4 3 2 1
//help: 5
return res;
}
public int top() {
while(queue.size() > 1){
help.offer(queue.poll());
}
int res = queue.poll();
help.offer(res);
Queue<Integer> temp = queue;
queue = help;
help = temp;
return res;
}
public boolean empty() {
return queue.isEmpty();
}
}
/**
* Your MyStack object will be instantiated and called as such:
* MyStack obj = new MyStack();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.top();
* boolean param_4 = obj.empty();
*/
思路:主要是用两个栈,一个用来存数据stackData,一个用来存最小值信息
//最小栈
class MinStack {
private Stack<Integer> stackData;
private Stack<Integer> stackMin;
public MinStack() {
stackData = new Stack<Integer>();
stackMin = new Stack<Integer>();
}
public void push(int val) {
if(stackMin.isEmpty()){
//存放最小值的栈为空,直接放入val
stackMin.push(val);
} else if(val <= getMin()){
//如果stackMin不为null,比较,比min小就压栈
stackMin.push(val);
}
stackData.push(val);
}
public void pop() {
if(stackData.isEmpty()){
throw new RuntimeException("your stak is empty");
}
int val = stackData.pop();
if(val == getMin()){
//将要弹出的值与最小值相同,stackMin也应当对应弹出
stackMin.pop();
}
// return val;
}
public int top() {
if(stackData.isEmpty()){
throw new RuntimeException("your stack is empty");
}
int val = stackData.pop();
stackData.push(val);
return val;
}
public int getMin() {
if(stackMin.isEmpty()){
throw new RuntimeException("your stack is empty");
}
int val = stackMin.pop();
stackMin.push(val);
return val;
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
在常见算法题中,我们难免会遇到很多递归的问题,那么这时我们该如何估计时间复杂度呢?
Master公式就能很好帮我们估计。
Master公式:
T(N) = a* T(N/b) + O(N ^d) (其中,a、b、d都是常数)的递归函数,我们可以直接通过Master公式来确定时间复杂度
例如:在[0,N]范围上求最大值,使用递归(分为left与right):T(N) = 2 * T(N/2) + O(1)
因为每次是分两半,因此:b=2,因为有两部分,因此a=2,O(1)是因为有一个比较的流程
假如是分为1/3、2/3,结果就为:T(N) = T(N/3) + T(2N/3) + O(1)
不管是什么具体实现,只要是有序表,都有一下固定的基本功能和固定时间复杂度:
总结:
哈希表在使用时,增删改查时间复杂度都是O(1)
有序表在使用时,比哈希表功能多,时间复杂度都是O(logN)