一. 链表
二. 队列
三. 栈
JZ3 从尾到头打印链表
JZ14 链表中倒数第k个结点
JZ15 反转链表 链表
JZ16 合并两个排序的链表
JZ25 复杂链表的复制
JZ26 二叉搜索树与双向链表
JZ36 两个链表的第一个公共结点
JZ46 孩子们的游戏(圆圈中最后剩下的数)
JZ55 链表中环的入口结点
JZ56 删除链表中重复的结点
JZ62 二叉搜索树的第k个结点
牛客剑指offer题目
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
//返回链表中的值就可以了,考虑用栈
ArrayList<Integer> result = new ArrayList<>();
Stack<Integer> stack=new Stack<>();
while( listNode != null){
stack.push(listNode.val);
listNode=listNode.next; //记得传完值以后,要让节点指向下一个节点啊
}
while(!stack.isEmpty()){
result.add(stack.pop());
}
return result;
}
}
import java.util.ArrayList;
以及import java.util.Stack;
牛客剑指offer题目
public ListNode FindKthToTail(ListNode head,int k){
//假设6个节点,1 2 3 4 5 6 倒数第2个,就是正数第5个,k=2,求正数第5个节点
//先求链表长度
int size=0;
ListNode pHead=head;
while(pHead!=null){
size++;
pHead=pHead.next;
}
if(k>size) return null; //注意判断k是否超出范围啊
else{
int head_num=size+1-k; //6+1-2=5
pHead=head;
for(int i=1;i<head_num;i++){
pHead=pHead.next;
}
return pHead;
}
}
牛客剑指offer题目
public ListNode ReverseList(ListNode head){
//需要三个节点之间得交换来反转链表
ListNode pre=null;
ListNode next=null;
while(head!=null){//head没到null就说明,head指向得不是最后一个节点,就要一直交换
next=head.next;//首先head指向得是当前节点,那么在最开始head是头节点得时候,pre=null没问题,但是next不应该是null,所以先给next赋值
head.next=pre;//head是当前节点,因为要反转链表,所以当前节点得next应该是前一个节点,也就是pre
pre=head; //前两行代码就算转换完毕了,接下来就是移动节点,所有得节点后移,pre就应该是刚才得当前节点
head=next; //next应该给当前节点赋值
}
return pre;
//最终返回得应该是pre,因为当head指向到最后一个节点的时候,head并不是null,循环执行以后,head指向了null,
// 在判断发现循环条件不满足,
// 但是此时的head指向的是null,pre才是最后一个节点,也就是现在的头节点,所以返回pre
}
牛客剑指offer题目
public ListNode Merge(ListNode list1,ListNode list2){
ListNode list=null;
if(list1==null){
return list2;
}else if(list2==null){
return list1;
}
if(list1.val<=list2.val){
// list.val=list1.val;
list=list1; // 上面那样写法不对,因为list最开始是null,所以不存在val,
//你要直接将list1给list,然后再修改next得值
list.next=Merge(list1.next,list2);
}
if(list1.val>list2.val){
// list.val=list2.val;
list=list2; // 上面那样写法不对,因为list最开始是null,所以不存在val,
//你要直接将list2给list,然后再修改next得值
list.next=Merge(list1,list2.next);
}
return list;
}
牛客剑指offer题目
//要想复制得到新的链表,首先你得创建一个新的链表,而且新的链表要和原链表建立起来联系
public RandomListNode Clone(RandomListNode pHead){
//先利用hashmap建立起来原链表和新链表的关系
HashMap<RandomListNode,RandomListNode> map=new HashMap<>();//建立原链表与新链表的关系
RandomListNode p1=pHead;////用来创建新旧链表的对应节点关系
while(p1!=null){
map.put(p1,new RandomListNode(p1.label)); //把label逐个存入到新的链表中
p1=p1.next;
}//至此,新链表和旧链表关系创建成功,但是新链表只有label值,next和random都还为null
RandomListNode p2=pHead;// 用来存储next和random指针
while(p2!=null){
//由于最后一个节点的next可能为null,所以需要判断一下,是否是最后一个节点
//但是random就没有为null的节点,所以不需要判断
if(p2.next!=null){
map.get(p2).next=map.get(p2.next); //map.get(p2)表示得到新链表的节点,这个节点的next值应当存储下一个节点,
// 下一个节点就应该是刚才建立的新节点new RandomListNode的第二个节点
}else{
map.get(p2).next=null;
}
map.get(p2).random=map.get(p2.random);
p2=p2.next;
}
return map.get(pHead);
}
牛客剑指offer题目
ArrayList<TreeNode> list=new ArrayList<>();
public TreeNode Convert(TreeNode pRootOfTree){
if(pRootOfTree == null) return null;
//二叉搜索树,左子树的所有节点的值小于根节点的值,右子树所有节点的值大于根节点的值,左<根<右
//将二叉搜索树变为排序好的链表实际上就是中序,然后将中序的结果串起来
Trans(pRootOfTree); //得到排序好的list
//2 5 9 串联起来
for(int i=0;i<list.size()-1;i++){
list.get(i).right=list.get(i+1);//当前值的右节点 存的应该是list中的下一个值
list.get(i+1).left=list.get(i); //i+1 的left存的是前一个节点信息
//这样写的话,就不涉及到特殊的第一个节点和最后一个节点了
}
return list.get(0);
}
public void Trans(TreeNode pRootOfTree){
if(pRootOfTree==null) return ;
else {
Trans(pRootOfTree.left);
list.add(pRootOfTree);
Trans(pRootOfTree.right);
}
}
牛客剑指offer上题目
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2){
//求2个链表的长度
int count1=0;
ListNode p1=pHead1;
while (p1!=null){
count1++;
p1=p1.next;
}
int count2=0;
ListNode p2=pHead2;
while (p2!=null){
count2++;
p2=p2.next;
}
//让长的链表先走 两个长度之差的 步数
int sub=count1-count2;
p1=pHead1;
p2=pHead2;
if(sub>0){ //说明链表1长
for(int i=0;i<sub;i++){
p1=p1.next;
}
}else {
sub=-sub;
for (int i=0;i<sub;i++){
p2=p2.next;
}
}
//2个链表一起走,相同的那个节点就作为返回值
while (p1!=null && p2!=null){
if(p1==p2) return p1;
p1=p1.next;
p2=p2.next;
}
return null;
牛客网上剑指offer题目
public int LastRemaining_Solution(int n, int m){
if(n<1||m<1) return -1; //注意特殊情况
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<n;i++){
list.add(i);
} //将孩子序号存入到一个list中 [0 1 2 3 4 5 6] m=3
int cur=-1; //指向当前指针
while(list.size()>1){
for(int i=0;i<m;i++){//控制循环次数,每到这个循环执行一次删除一个元素
cur++;
if(cur==list.size()) cur=0;
}//循环执行完一遍,删除一个元素
list.remove(cur);//删除这个元素以后,cur需要变化一下 删除2以后,变为[0 1 3 4 5 6] 此时cur=2,但是下一次循环中需要cur为2,所以这里先递减一次
cur--;
}
return list.get(0);
}
牛客网上剑指offer题目
if(pHead == null){
return null;
}
// 判断是否是环,两个指针从头节点开始,slow一次走一个,fast一次走俩个,
//如果是环,fast总会返回来和slow相遇,如果不是环,fast走到尾节点也不会和slow相遇
boolean isHuan = false; //默认不是环
ListNode slow=pHead;
ListNode fast=pHead;
while(fast!=null && fast.next!=null){ //fast没到尾节点,就一直往后走,到尾节点,也没相遇,那就不是环
slow=slow.next;
fast=fast.next.next;
if(slow == fast){
isHuan = true;
break;
}
}
//如果不是环,那么返回null
if(!isHuan) {
return null;
}else{
//如果是环那么,确定环中节点的个数,刚才相遇的节点一定是环中的节点,
//所以从这个节点开始,在回到这个节点所走的步数也就是环中节点的个数了
int n=1;
// fast和slow现在指的是同一个节点,相遇的那个节点
fast=fast.next;
while(fast!=slow){
fast=fast.next;
n++;
} //n是环中节点的个数
//找到节点的入口,一个节点从头开始,另一个节点从节点长度那么长开始,相遇的节点就是环的入口
slow = pHead;
fast = pHead;
for(int i=0;i<n;i++){
fast=fast.next;
}
//然后每个节点走一步,直到相遇,则为节点的入口
while(slow!=fast){
slow=slow.next;
fast=fast.next;
}
}
return fast;
}
牛客网上剑指offer答案
思路:
有一点很重要的是,循环链表的时候,需要注意最后一个节点,当p指向最后一个节点的时候,p的next是null,如果你使用p.next!=null
作为判断条件是可以的,但是如果你后面写p.next.val会发生异常,因为没有val这个值,所以有时我们考虑的是判断while (p.next!=null)
先将重复的元素存入到set集合中。然后先处理头节点,看看头节点是否是重复元素。然后遍历原链表,将原链表中的元素逐个取出来,看看set中是否有,如果有,新链表中我们不存这个元素,跳过这个元素,set中没有这个元素,新链表中就要这个元素。
代码:
public ListNode deleteDuplication(ListNode pHead){
HashSet<Integer> set=new HashSet<>();
ListNode p=pHead;
while (p.next!=null){
//到最后一个指针的时候,是p.next为null,因为后面用到了p.next.val,空指针是没有val的,所以这里只能写p.next
if(p.val == p.next.val) set.add(p.val);
p=p.next;
}
//重新组合一个链表作为返回值,首先单独处理头节点
while (pHead!=null && set.contains(pHead.val)){
pHead=pHead.next;
}
if(pHead==null) return null;
ListNode pre=pHead;
ListNode cur=pHead.next;
while (cur!=null){
if(set.contains(cur.val)){
pre.next=cur.next;
cur=cur.next;
}else{
pre=pre.next;
cur=cur.next;
}
}
return pHead;
}
牛客网上剑指offer题目
ArrayList<TreeNode> list=new ArrayList<>();
TreeNode KthNode(TreeNode pRoot, int k){
Trans(pRoot);
if(k>=1 && list.size()>=k) {
return list.get(k-1); //链表的大小是可以直接利用函数的,不用自己遍历求啊、
}
return null;
}
public void Trans(TreeNode root){ //中序遍历
if(root == null) return ;
Trans(root.left);
list.add(root);
Trans(root.right);
}
JZ5 用两个栈实现队列
JZ22 从上往下打印二叉树
JZ59 按之字形顺序打印二叉树
JZ60 把二叉树打印成多行
JZ61 序列化二叉树
牛客剑指offer题目
思路:
栈是先进后出,队列是先进先出。用两个栈实现队列的,入队和出队函数。
入队,没什么好说的,就直接放入元素就可以了,也不需要返回值,所以直接存到stack1中记录即可。
主要是出队的时候,注意是要先入队的先出,所以需要将元素从stack1中导入到stack2中,然后再tsack2出队,出队元素就是stack2中的顶级元素,这个元素是最先进入栈中的那个元素。
还有一点需要注意的是,出队以后,方便其他元素下一次入队,是进入到stack1中,所以出队以后,需要将stack2中的元素再放回stack1中。
代码:
//第5题:用两个栈实现队列
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node); //入队,没什么好说的,就直接放入元素就可以了,也不需要返回值,所以直接存到stack1中即可
}
public int pop() {
//主要是出队的时候,注意是要先入队的先出,所以需要将元素从stack1中导入到stack2中,然后再出队,出队元素就是stack2中的顶级元素
//还有一点需要注意的是,出队以后,方便下一次入队,是进入到stack1中,所以出队以后,需要将stack2中的元素再放回stack1中
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
int result= stack2.pop();
while(!stack2.isEmpty()){
stack1.push(stack2.pop());
}
return result;
}
//第5题补充,两个队列实现栈
Queue<Integer> queue1=new LinkedList<>();
Queue<Integer> queue2=new LinkedList<>();
public void push(int node) {//压入元素,没什么好说的和栈的操作一样,先随便用一个队列存放压入的元素
queue1.offer(node);
}
public int pop(){
//出栈的时候,需要注意q1中入队1 2 3 出栈应该出3,所以我们一个一个出队,不是顶层的元素就出队之后存放到q2中,
// 出掉3以后,再将q2中的元素存回q1中,方便下一次其他元素入队
while (queue1.size()!=1){
queue2.offer(queue1.poll());
}
int result=queue1.poll();
while(!queue2.isEmpty()){
queue1.offer(queue2.poll());
}
return result;
}
也不确定代码错还是队,哪天遇到题验证一下吧。
牛客剑指offer题目
//从上往下打印二叉树
//打印链表需要借助队列
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> list=new ArrayList<>();
if(root==null) return list;
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
TreeNode temp=queue.poll();
list.add(temp.val);
// queue.offer(temp.left);
// queue.offer(temp.right);
// 这样写是错的,一定要先判断是否为空再offer值,不然offer,提供的是null
if(temp.left!=null) queue.offer(temp.left);
if(temp.right!=null) queue.offer(temp.right);
}
return list;
}
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
if(pRoot == null) return result;
Queue<TreeNode> q=new LinkedList<TreeNode>(); //存储节点
q.offer(pRoot);
boolean rev=false;//是否反转存入元素
while(!q.isEmpty()){
ArrayList<Integer> list=new ArrayList<>();
int size = q.size(); //每行中 队列元素的个数
for(int i=0;i<size;i++){
TreeNode temp = q.poll();
if(temp == null) continue;
if(!rev){
list.add(temp.val);
}else{
list.add(0,temp.val);
}
q.offer(temp.left); //把元素的值存入进去以后就要把他的节点送入队列中,
//这一行的size已经确定了,送进队列中不会影响这一行的取值,下一行会重新判断size的
q.offer(temp.right);
}
if(list.size()>0){ //注意如果list中没有元素了,就不要存入元素了啊,所以需要判断一下
result.add(list);
}
rev = !rev;
}
return result;
}
牛客网上剑指offer题目
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> result = new ArrayList<>(); //result里面存的是list
if(pRoot == null) return result;
Queue<TreeNode> q = new LinkedList<>(); //用来存储每行的节点,逐个存入,逐个冒出
q.offer(pRoot);
while(!q.isEmpty()){ //队列不为空的时候才能往外取元素
ArrayList<Integer> list=new ArrayList<>(); //用来存储每行的元素
//因为每一行的元素是独立的,所以要提前定义好一个size,在取元素的时候,取走一个要存入这个元素的左右节点,
//所以提前定义size,取出size大小的元素即可
int size=q.size();
for(int i=0;i<size;i++){
TreeNode temp=q.poll(); //把每行中元素,从队列的元素取出来
//由于一行中不一定有几个节点,所以定义一个临时变量temp逐个取
list.add(temp.val); //取出节点以后,要把这个节点的左右节点存到队列中,下一次循环用到
if(temp.left != null) q.offer(temp.left);
if(temp.right != null) q.offer(temp.right);
}
if(!list.isEmpty()){ //如果链表不空,才放到result中
result.add(list);
}
}
return result;
}
牛客网上剑指offer题目
不会,有点不理解
JZ20 包含min函数的栈
JZ21 栈的压入、弹出序列
JZ5 用两个栈实现队列
JZ62二叉搜索树的第k个结点
牛客剑指offer题目
//包含min函数的栈
//有两种方法:一种是借助辅助栈使用剑指offer书上的方法
//一种是使用迭代器的方法
//使用迭代器的方法比较简单,先使用迭代器的方法:
Stack<Integer> stack=new Stack<>();
public void push(int node) {
stack.push(node);
}
public void pop() {
stack.pop();
}
public int top() {
return stack.peek();
}
public int min() {
//迭代器的使用,需要导包
Iterator<Integer> it =stack.iterator();
int min=stack.peek();
int tmp=0;
while (it.hasNext()){
tmp=it.next();//只是用一次
if(min>tmp) min=tmp;
}
// Iterator it =stack.iterator();
// int min=stack.peek();
// while (it.hasNext()){
// if(min>it.next()) min=it.next();
// } // 错误示范。错误原因:每次使用it.next都会是下一个元素了,所以循环中多次使用next肯定是有问题的
return min;
}
牛客剑指offer题目
思路:
栈中每次存入一个元素,就和popA去比较,相同就弹出,且popA进入下一个元素
如果栈顶元素和popA的首个元素相等,那么需要出栈,
出栈以后是要继续判断栈顶元素和popA的下一个元素是否相等的,
如果相等要继续出栈,不相等才进行下一轮的进栈,
代码:
public boolean IsPopOrder(int [] pushA,int [] popA){
// 对于栈来说,长度是属性
if(pushA.length == 0 || popA.length== 0 || pushA.length != popA.length){
return false;
}
int j=0;
Stack<Integer> stack=new Stack<>();
for(int i=0;i<pushA.length;i++){
stack.push(pushA[i]); //先把元素放入栈中,看看能不能把栈中的元素清空,能清空返回true
//栈中每次存入一个元素,就和popA去比较,相同就弹出,且popA进入下一个元素
//如果栈顶元素和popA的首个元素相等,那么需要出栈,
//出栈以后是要继续判断栈顶元素和popA的下一个元素是否相等的,
//如果相等要继续出栈,不相等才进行下一轮的进栈,
//所以这里需要的应该是一个循环来进行 多次的判断
while(!stack.isEmpty() && stack.peek()==popA[j]){
stack.pop();
j++;
}
}
return stack.isEmpty();
}
牛客剑指offer题目
思路:
栈是先进后出,队列是先进先出。用两个栈实现队列的,入队和出队函数。
入队,没什么好说的,就直接放入元素就可以了,也不需要返回值,所以直接存到stack1中记录即可。
主要是出队的时候,注意是要先入队的先出,所以需要将元素从stack1中导入到stack2中,然后再tsack2出队,出队元素就是stack2中的顶级元素,这个元素是最先进入栈中的那个元素。
还有一点需要注意的是,出队以后,方便其他元素下一次入队,是进入到stack1中,所以出队以后,需要将stack2中的元素再放回stack1中。
代码:
//第5题:用两个栈实现队列
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node); //入队,没什么好说的,就直接放入元素就可以了,也不需要返回值,所以直接存到stack1中即可
}
public int pop() {
//主要是出队的时候,注意是要先入队的先出,所以需要将元素从stack1中导入到stack2中,然后再出队,出队元素就是stack2中的顶级元素
//还有一点需要注意的是,出队以后,方便下一次入队,是进入到stack1中,所以出队以后,需要将stack2中的元素再放回stack1中
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
int result= stack2.pop();
while(!stack2.isEmpty()){
stack1.push(stack2.pop());
}
return result;
}
//第5题补充,两个队列实现栈
Queue<Integer> queue1=new LinkedList<>();
Queue<Integer> queue2=new LinkedList<>();
public void push(int node) {//压入元素,没什么好说的和栈的操作一样,先随便用一个队列存放压入的元素
queue1.offer(node);
}
public int pop(){
//出栈的时候,需要注意q1中入队1 2 3 出栈应该出3,所以我们一个一个出队,不是顶层的元素就出队之后存放到q2中,
// 出掉3以后,再将q2中的元素存回q1中,方便下一次其他元素入队
while (queue1.size()!=1){
queue2.offer(queue1.poll());
}
int result=queue1.poll();
while(!queue2.isEmpty()){
queue1.offer(queue2.poll());
}
return result;
}
也不确定代码错还是队,哪天遇到题验证一下吧。