算法学习03:栈和队列

栈和队列

用数组结构实现大小固定的队列和栈

  • 栈结构很好实现,准备变量index,指向栈顶
    • 当压入一个数,index++
    • 当弹出一个数,index- -
    • 越界条件: index超了
  • 队列结构的实现: 循环队列
    • 一般的循环队列有两个指针: start和end
    • 加以改进的循环队列: 再加一个变量size = end-start. 用size来解耦start和end,让end与start之间不再发生关系.

最小栈leetcode 155

最小栈的pop(),push(),getMin()操作的时间复杂度都是O(1)
解法:

  1. 存储两个栈,data栈和min栈,data栈存储压入的数据,min栈存储当前栈中最小值
  2. 当向data压入一个数时,与min栈栈顶比较,若被压入的数比min栈顶小,则向min栈压入新数,否则向min栈重复压入min栈顶.

栈和队列的相互实现leetcode 232,leetcode 225

用队列实现栈: 前边所有数都入队列,留着最后一个数给用户返回.
用栈实现队列: 创建两个栈:push栈和pop栈,进队列时进push栈,出队列时从pop栈往外拿. 中间可能要把数据从一个栈倒入另一个栈,每次倒数据的时候要直接倒完.

宏观划分问题

对于一些问题,其问题需要不断的考虑边界问题,比较难想,这样我们就遵循先宏观再微观的思路.
先提取出宏观思路来把问题划分成微观步骤,然后通过函数考虑局部的具体实现.

转圈遍历矩阵leetcode 54

将遍历一圈提取成printCircle()函数,然后从左上角和右下角开始,每打印一圈两个角向里侧移动一位,直到两个角相遇.
printCircle()要考虑边界情况: 左上角和右下角位于同一行或同一列.

旋转正方形矩阵leetcode 48

将遍历一圈提取成rotateCircle()函数,然后从左上角和右下角开始,每打印一圈两个角向里侧移动一位,直到两个角相遇.
rotateCircle()要考虑边界情况: 左上角和右下角相遇,实际只有一个元素,不用旋转.

“之”字形打印矩阵

将打印一条斜线提取成writeLine()函数,接收斜线的两个顶点和打印方向. 初始斜线的顶点应该为矩阵左下角,每打印一行,顶点向右下移动一位,且方向转变一次.

最优解来自于数据状况或问法

在行列都排好序的矩阵中找数(leetcode 74)

  • 这个数据状况比较特殊,可以帮助我们找到解法:
    • 每一个数都与其同一行和同一列的数有明确的大小关系,而与其斜对角线上的数没有大小关系.因此每次比较大小后应该向其同一行或同一列转移,下面考虑从哪里开始比较.
    • 若从左上(最小值)或者右下(最大值)比较后,有两个可转移的方向,因此我们从右上角或左下角开始比较.
  • 从右上角开始找,令指针指向右上角的节点,
    • 若一个数比目标大,则该数以下必不是目标,因此指针向左移动一位;
    • 若一个数比目标小,则该数以左必不是目标,因此指针向下移动一位.
  • 注意指针不越界边界条件:((curY < rowNum) && (curX >=0)),一边是等于,一边是不等于.另外对二维数组进行索引时先索引y,后索引x,matrix[y][x]

推广: 双指针问题leetcode 15

  • 问题:leetcode中有一系列k-sum问题,判断数组中能否找出k个数之和为目标aim?
  • 思路:使用双指针扫描方法可以将问题降维: 先将数组排序,然后两个指针分别指向数组首尾向中间逼近,不断判断两指针之和是否为目标aim,这样3sum可以化成2sum,4sum可以化成3sum.
  • 实现:2sum的执行过程如下:
  1. 现将数组排序,设定两个指针p1p2,分别指向数组的第一个元素和最后一个元素.
  2. 计算两个元素的和p1+p2
    1. 若其和大于目标aim,则p2向前移动一位.
    2. 若其和小于目标aim,则p1向后移动一位.
    3. 若其和等于目标aim,则找到解,返回两个指针.
  3. p1p2相遇,则返回未找到,否则继续步骤2.
  • 证明: 数组中任意元素两两相加构成一张表,其横坐标为第一个加数,纵坐标为第二个加数,在这个表中查找目标值aim.这个问题就转化为上边的在行列有序的表中查找值的问题了.
    初始状态下,p1指向数组首,p2指向数组末尾,相当于这个二维表左下角的点.p1向后移动对应二维表中向右移动.p2向前移动对应二维表中向上移动.
    假 设 横 纵 坐 标 分 别 为 1 − 4 [ 1 2 3 4 2 3 4 5 3 4 5 6 4 5 6 7 ] 假设横纵坐标分别为1-4\\ \begin{bmatrix} 1 &amp; 2 &amp; 3 &amp; 4 \\ 2 &amp; 3 &amp; 4 &amp; 5 \\ 3 &amp; 4 &amp; 5 &amp; 6 \\ 4 &amp; 5 &amp; 6 &amp; 7 \\ \end{bmatrix} 141234234534564567

链表问题:笔试与面试要求不同

链表的题目时间复杂度难以优化,关键优化的点在于空间复杂度.
因此笔试链表,优先考虑如何过掉这道题.
面试链表,要注意考虑用更少空间复杂度.

判断一个链表是否为回文结构

快慢指针:
快指针指向末尾,慢指针走向中点.然后把右半部分逆序.
之后从两头开始向中间走,比较是否相等
最后在把右半部分逆序回来.
注意:

  1. 链表长度为偶数或者奇数会对两个指针产生影响.
  2. 反转链表函数中要注意最后把链表头结点的next置空.

将单向链表按某值划分成左边小、中间相等、右边大的形式(荷兰国旗问题)

准备成数组.然后再穿起来.
但是荷兰国旗问题不保证稳定性,且要O(N)空间

设置三个指针节点:equal,more,less
遍历一遍数组,把相应的节点挂在三个指针后边.
注意扣边界: 有个区域没节点.

复制含有随机指针节点的链表leetcode 138

遍历两次链表,第一趟复制表的next结构,第二趟复制表的random指针.
具体实现有两种方式:

  1. 使用map,较简单
  2. 在原链表每个节点后面复制出新节点.

    注意:
    1. 复制之后再遍历链表的话需要指针每次转移跳两格,要注意是否产生空指针错误,要将cur.next = cur.next.next;改为cur.next = (cur.next != null) ? cur.next.next : null;
    2. 注意有一些节点的random指针指向空,这种复制的时候要格外小心,别发生空指针错误.cur.next.random = (cur.random != null) ? cur.random.next : null;

两个单链表相交的一系列问题

问题: 判断两个链表是否相交,若相交,返回首个交点. 其中两个链表都可能有环或者无环.
转化: 该问题可以转化为三个小问题:

  1. 判断链表是否有环,若有环返回入环节点,(快慢指针方法: 快指针每次走两步,慢指针每次走一步,直到相交. 然后新建两个指针,分别指向交点指向头结点,两个指针同步后移直到他们相遇,相遇点为入环节点)leetcode 142
  2. 判断两个无环链表的首个交点(先遍历两个链表,得出长度差值,根据差值对齐链表,然后两个头指针同步后移直到相遇)leetcode 160
  3. 判断两个可能有环链表的首个交点
    解法
    1. 先找到两个列表是否有环,并返回入环点.
      1. 若两列表都无环,则转化为无环链表求交点问题.
      2. 若一个链表有环,一个链表无环,则它们不可能相交.(反证:若相交,则无环链表最终将进环)
      3. 若两链表都有环,则先比较两入环点是否相同
        1. 若两入环点相同,则说明入环以前两链表已经相交,所以对于入环前节点可以简化为无环链表求交点问题.
        2. 若两入环点不同,则比较两入环点是否相连:
          1. 若两入环点不相连,说明两链表无交点(一个链表不可能有两个环)
          2. 若两入环点相连,则他们都是最早交点(这种情况下无非是哪个离两链表较近的问题了,无所谓首个交点)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值