目录
一、栈
用两个栈实现队列
用两个栈来实现一个队列,使用n个元素来完成 n 次在队列尾部插入整数(push)和n次在队列头部删除整数(pop)的功能。 队列中的元素为int类型。保证操作合法,即保证pop操作时队列内已有元素。
数据范围: n\le1000n≤1000
要求:存储n个元素的空间复杂度为 O(n)O(n) ,插入与删除的时间复杂度都是 O(1)O(1)
示例1
输入:
["PSH1","PSH2","POP","POP"]返回值:
1,2说明:
"PSH1":代表将1插入队列尾部 "PSH2":代表将2插入队列尾部 "POP“:代表删除一个元素,先进先出=>返回1 "POP“:代表删除一个元素,先进先出=>返回2示例2
输入:
["PSH2","POP","PSH1","POP"]返回值:
2,1
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.size()<=0){
while(stack1.size()!=0){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
每一回pop的时候都会先看栈2中的值是否为空,如果为空就栈1pop操作,从栈顶到栈底将数据添加到栈2中,模拟了一个先进先出的队列
二、链表
反转链表
描述
给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。
数据范围: 0\leq n\leq10000≤n≤1000
要求:空间复杂度 O(1)O(1) ,时间复杂度 O(n)O(n) 。
如当输入链表{1,2,3}时,
经反转后,原链表变为{3,2,1},所以对应的输出为{3,2,1}。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head==null){
return head;
}
ListNode pre=null;
ListNode cur=head;
while(cur!=null){
ListNode temp=cur.next;
cur.next=pre;
pre=cur;
cur=temp;
}
return pre;
}
}
例如链表是以下内存地址结构1->2->3->4->5->6->null,引入pre之后可以视作null 1->2->3->4->5->6,cur指向链表的头结点1,每次进入循环temp指向的都是cur的下一个结点,此时就是2,cur.next=pre执行过后内存地址结构转为null<-1 2->3->4->5->6->null,可以看到1结点指向了null,pre=cur,pre此时就是1结点,cur=temp,cur就变成了2结点,执行第二次循环就变成了:
null<-1<-2 3->4->5->6->null,一直循环直到最终结果
从尾到头打印链表
描述:输入一个链表的头节点,按链表从尾到头的顺序返回每个节点的值(用数组返回)。
如输入{1,2,3}的链表如下图:
返回一个数组为[3,2,1]
0 <= 链表长度 <= 10000
0 <= 链表长度 <= 10000
import java.util.*;
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list = new ArrayList<>();
ListNode tmp = listNode;
while(tmp!=null){
list.add(0,tmp.val);
tmp = tmp.next;
}
return list;
}
}
经过测试使用list.add(0,x)该方法,打印的时候先添加的数据后打印,ArrayList就是一个可以动态修改的数组
合并两个排序链表
描述
输入两个递增的链表,单个链表的长度为n,合并这两个链表并使新链表中的节点仍然是递增排序的。
数据范围: 0 \le n \le 10000≤n≤1000,-1000 \le 节点值 \le 1000−1000≤节点值≤1000
要求:空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)如输入{1,3,5},{2,4,6}时,合并后的链表为{1,2,3,4,5,6},所以对应的输出为{1,2,3,4,5,6},转换过程如下图所示:
或输入{-1,2,4},{1,3,4}时,合并后的链表为{-1,1,2,3,4,4},所以对应的输出为{-1,1,2,3,4,4},转换过程如下图所示:
示例1
输入:
{1,3,5},{2,4,6}返回值:
{1,2,3,4,5,6}
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null){
return list2;
}
if(list2==null){
return list1;
}
ListNode result = new ListNode(-1);
ListNode guard = result;//哨兵节点,很重要
while(true){
if(list1 == null && list2 == null){//全部取完了,可以结束了。
break;
}
if(list1==null){//list1取完了,后面的全部取list2的。
result.next = list2;
list2 = list2.next;
result = result.next;
}else if(list2 == null){//list2取完了,后面全部取list1的。
result.next = list1;
list1 = list1.next;
result = result.next;
}else if(list1.val<=list2.val){//和数组一样,谁小取谁的
result.next = list1;
list1 = list1.next;
result = result.next;
}else if(list1.val>list2.val){//和数组一样,谁小取谁的
result.next = list2;
list2 = list2.next;
result = result.next;
}
}
return guard.next;
}
创建一个新的链表,哪一个list的当前节点小,就把该list当前节点放进新的链表,这是一个有去无回的过程,所以需要一个哨兵提前将链表头节点保存起来。
删除链表 (go)
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func deleteNode(head *ListNode, val int) *ListNode {
if(head.Val==val){
return head.Next
}
pre:=head
for head.Next!=nil && head.Next.Val!=val {
head=head.Next
}
if(head.Next!=nil){
head.Next=head.Next.Next
}
return pre
}
如果链表第一个节点值就等于给定值,直接返回头节点下一个节点,相当于把头节点删掉,pre作为哨兵,退出循环有两种方式一个是循环到链表尾,一个是循环到与给定值相同的节点退出,如果没到链表尾就将目前的节点的next指向下一个节点的下一个节点,相当于把该节点的下一个节点在链表中删除