剑指offer算法java版(更新中)

这篇博客介绍了如何使用两个栈实现队列,包括其空间和时间复杂度要求,并提供了Java实现代码。此外,还详细讲解了链表的反转、从尾到头打印链表以及合并两个排序链表的问题,每个问题都给出了相应的解决方案和示例。这些操作在数据结构和算法中具有重要意义,展示了高效的数据操作技巧。
摘要由CSDN通过智能技术生成

目录

一、栈

用两个栈实现队列

二、链表

反转链表

 从尾到头打印链表

合并两个排序链表


一、栈

用两个栈实现队列

用两个栈来实现一个队列,使用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指向下一个节点的下一个节点,相当于把该节点的下一个节点在链表中删除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值