Python一皮肤电信号滤波相关的函数

(HDU)Turing Tree(离线+线段树)

  返回  

剑指Offer(中等)

2021/8/21 17:35:01 浏览:

JZ1 二维数组中的查找

分析代码

利用该二维数组的性质:

  • 每一行都按照从左到右递增的顺序排序,
  • 每一列都按照从上到下递增的顺序排序

改变个说法,即对于左下角的值 m,m 是该行最小的数,是该列最大的数
每次将 m 和目标值 target 比较:

  1. 当 m < target,由于 m 已经是该行最大的元素,想要更大只有从列考虑,取值右移一位
  2. 当 m > target,由于 m 已经是该列最小的元素,想要更小只有从行考虑,取值上移一位
  3. 当 m = target,找到该值,返回 true

用某行最小或某列最大与 target 比较,每次可剔除一整行或一整列

public class Solution {
    public boolean Find(int target, int [][] array) {
        int rows = array.length;
        if(rows == 0){
            return false;
        }
        int cols = array[0].length;
        if(cols == 0){
            return false;
        }
        // 左下
        int row = rows-1;
        int col = 0;
        while(row>=0 && col<cols){
            if(array[row][col] < target){
                col++;
            }else if(array[row][col] > target){
                row--;
            }else{
                return true;
            }
        }
        return false;
    }
}

JZ14 链表中倒数最后k个结点

双指针

这题要求链表的倒数第k个节点,最简单的方式就是使用两个指针,第一个指针先移动k步,然后第二个指针再从头开始,这个时候这两个指针同时移动,当第一个指针到链表的末尾的时候,返回第二个指针即可。注意,如果第一个指针还没走k步的时候链表就为空了,我们直接返回null即可。

public ListNode FindKthToTail (ListNode pHead, int k) {
        // write code here
        ListNode first = pHead;
        for(int  i= 0;i<k;i++){
            if(first == null) return first;
            first = first.next;

        }
        ListNode last = pHead;
        while(first!=null){
            first = first.next;
            last = last.next;
        }
        return last;
    }
}

递归

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param pHead ListNode类
     * @param k int整型
     * @return ListNode类
     */
    int size=0;
    public ListNode FindKthToTail (ListNode pHead, int k) {
        // write code here
        if(pHead==null){
            return pHead;
                 
        }
        ListNode node =FindKthToTail( pHead.next,k);
        size++;
        if(size==k){
            return pHead;
        }
         
        return node;
    }
}

JZ62 二叉搜索树的第k个结点有点像。

JZ15 反转链表

迭代

不存放有作用的数据

作用就是表示单链表

public class Solution {
public class Solution {
    public ListNode ReverseList(ListNode head) {
      //如果当前链表为空,或者只有一个节点,无需反转,直接返回
		if(head == null) {
			return head;
		}
		
		//定义一个辅助的指针(变量),帮助我们遍历原来的链表
		ListNode cur = head;
		ListNode next = null;// 指向当前节点[cur]的下一个节点
		ListNode reverseHead = new ListNode(0);
		//遍历原来的链表,每遍历一个节点,就将其取出,并放在新的链表reverseHead 的最前端
		//动脑筋
		while(cur != null) { 
			next = cur.next;//1.先暂时保存当前节点的下一个节点,因为后面需要使用
			cur.next = reverseHead.next;//2.将cur的下一个节点指向新的链表的最前端。
//只是翻转放在reverseHead.next这个位置,如果没有reverseHead指向连接(第三步)reverseHead链表永远为空
			reverseHead.next = cur; //3.将cur 连接到新的链表上
			cur = next;//让cur后移
		}
        return reverseHead.next;
    }
}

递归

错误

 ListNode cur= new ListNode(0);//ya
     public ListNode ReverseList(ListNode head) {
         //定义一个辅助的指针(变量),帮助我们遍历原来的链表

         cur.next=head;

         // reverseHead = new ListNode(1);
         //如果当前链表为空,或者只有一个节点,无需反转,直接返回
         if(head == null||head.next==null) {
             return head;
         }

         ListNode reverseHead=ReverseList(cur.next);
         cur.next.next = cur;//后一个节点连接前一个节点
         cur.next = null;
         return reverseHead;

     }

 }

原因:递归不能用辅助指针,赋值错误。

正确

public class Solution {
public class Solution {
    public ListNode ReverseList(ListNode head) {
    if (head==null||head.next == null ) return head;
    ListNode last = ReverseList(head.next);
    head.next.next = head;    //**后一个节点连接前一个节点**
    head.next = null;
    return last;
    }
}

JZ21 栈的压入、弹出序列

错误

import java.util.ArrayList;
import java.util.Stack;
public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
       Stack<Integer> stack = new Stack();
      for(int i:popA){
          stack.push(i);
      }
        for(int i:pushA){
            if(i==stack.peek()){
                stack.pop();
            }
            
        }
        
        return stack.isEmpty();
    }

}

先全压再pop判断(错误)

正确

import java.util.*;

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
      Stack<Integer> stack = new Stack<>();
      int j = 0;
      for (int i = 0; i < pushA.length; i ++) {
          stack.push(pushA[i]);
          while(!stack.isEmpty() && stack.peek() == popA[j]){
              stack.pop();
              j ++;
          }
      }
        return stack.isEmpty();
    }
}

用while一边压一边弹出判断

直接模拟即可。因为弹出之前的值都会先入栈,所以这里用个栈来辅助。

例如 [1,2,3,4,5],[4,5,3,2,1]

JZ35 数组中的逆序对

题目地址

代码分析

对于一个包含N个非负整数的数组A[1…n],如果有i < j,且A[ i ]>A[ j ]

给定一个数组arr, 数组元素各不相同,求arr[i] > arr[j] 且 i < j的个数。

代码分析(归并排序)

JZ41 和为S的连续正数序列

题目地址

代码分析(双指针)

import java.util.ArrayList;
public class Solution {
    public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
       ArrayList<ArrayList<Integer>> result=new ArrayList<>();
        int pleft=1,pright=2;
        while(pright>pleft){
            int cursum=(pright+pleft)*(pright-pleft+1)/2;//等差公式
            if(cursum==sum){
                ArrayList<Integer> sequence=new ArrayList<>();
                for(int i=pleft;i<=pright;i++){
                    sequence.add(i);
                }
                result.add(sequence);
                pleft++;
            }else if(cursum<sum){
                pright++;
            }else{
                pleft++;
            }
        }
        return result;
    }
}

分析
分析题意:我们用两个指针l和 r 表示当前枚举到的以 l 为起点到 r 的区间,sum 表示 [l,r] 的区间和,由求和公式可 O(1) 求得为
sum = (l + r) * (r - l + 1) / 2 ,起始 l=1,r=2。

一共有三种情况:

如果 sum<target 则说明指针 r 还可以向右拓展使得 sum 增大,此时指针 r 向右移动,即 r+=1
如果 sum>target 则说明以 l 为起点不存在一个 r 使得 sum=target ,此时要枚举下一个起点,指针 l 向右移动,即l+=1
如果 sum==target 则说明我们找到了以 l 为起点得合法解 [l,r] ,我们需要将 [l,r] 的序列放进答案数组,且我们知道以
l 为起点的合法解最多只有一个,所以需要枚举下一个起点,指针 l 向右移动,即 l+=1
终止条件即为 l>=r 的时候,这种情况的发生指针 r 移动到了 (target/2) + 1 的位置,导致 l<r 的时候区间和始终大于 target。
这个l指针是左边界,而r指针是右边界,有滑动窗口那味道了,只是这个窗口长度是动态变化的。

JZ46 孩子们的游戏(圆圈中最后剩下的数)

题目地址

解法一(链表)

class Solution {
    public int lastRemaining(int n, int m) {
        ArrayList<Integer> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            list.add(i);
        }
        int idx = 0;
        while (n > 1) {
            idx = (idx + m - 1) % n;
            list.remove(idx);
            n--;
        }
        return list.get(0);
    }
}

解法二(数学 / 动态规划)

class Solution {
    public int lastRemaining(int n, int m) {
        int x = 0;
        for (int i = 2; i <= n; i++) {
            x = (x + m) % i;
        }
        return x;
    }
}

在这里插入图片描述

JZ55 链表中环的入口结点

解法一(双指针)

public ListNode EntryNodeOfLoop(ListNode pHead) {
    if(pHead == null) {
        return null;
    }
    // 设置快慢指针,快指针一次走两步,慢指针一次走一步
    ListNode fast = pHead;
    ListNode low = pHead;
    do {
        // 快指针先走一步
        fast = fast.next;
        // 若fast为null,表示没环,直接return空
        if(fast == null) {
            return null;
        }
        // 若不为null,再向前走一步
        fast = fast.next;
        // 慢指针向前走一步
        low = low.next;
    }while(fast != null && fast != low);

    // low指针指向链表头节点,fast指针不变,还是在相遇点(因为a==c)
    // 两个指针速度均为1,向前走,再次相遇的点就是环入口节点
    low = pHead;
    while(low != fast) {
        low = low.next;
        fast = fast.next;
    }
    return low;
}

https://www.cnblogs.com/tuyang1129/p/12194656.html
思路
在这里插入图片描述
我们可以设置两个指针求解此问题:一个快指针fast,每次向前走两个节点,一个慢指针low,每次向前走一个节点;我们首先需要知道一个结论:这两个指针一快一慢向前走,若链表有环,则两个指针一定会相遇;

这里先明确三个名称:

  • 头节点:表示链表的第一个节点;
  • 环入口节点:若链表中有环,则进入这个环的第一个节点;
  • 相遇点:快慢指针相遇的节点;

我们设置三个变量来求解这个问题:

  • 变量a:头节点环入口节点的距离;
  • 变量b:从环入口节点 沿链表方向前进到 相遇点的距离;
  • 变脸c:从相遇点 沿链表方向到 环入口节点的距离;

所以,链表的长度 = a + b + c,而环的长度就是b+c

从开始到两个指针相遇,慢指针low走过的距离为a+b,而快指针fast走过的距离为a + k×(b+c) + b,其中k是一个整数,且k>0(因为如果k==0,那快慢指针走过的距离就相等了);
快指针fast每次前进两个节点,而慢指针low每次前进一个节点,所以我们可以得到一个结论:相同时间内,快指针的走的路程是慢指针的两倍;

2×(a+b) == a + k × (b+c) + b;
2a + 2b == a + k × (b+c) + b;
a + b == k × (b+c);
a == k × (b+c) - b;
a == (k-1) × (b+c) + c;  // 最终结果

b+c是环的长度
所以a==c

解法二(set)

public class Solution {
    //第一个数出现第二次,一定是入口
    public ListNode EntryNodeOfLoop(ListNode pHead) {
        HashSet set=new HashSet();
        
        while(pHead!=null){
            if(set.contains(pHead)){
                return pHead;
            }
            else{
                set.add(pHead);
            }
            pHead=pHead.next;
        }
        return null;
    }
}

JZ54 字符流中第一个不重复的字符

题目地址

代码

import java.util.Queue;
import java.util.LinkedList;
import java.lang.Character;
public class Solution {
    //Insert one char from stringstream
     int[] charCnt = new int[128];
    Queue<Character> queue = new LinkedList<Character>();
    public void Insert(char ch) {
        if (charCnt[ch]++ == 0) //新来的单身字符,入队
            queue.add(ch);
    }
    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce() {
        Character CHAR = null;
        char c = 0;
        while (!queue.isEmpty()) {
            CHAR = queue.peek();
            c = CHAR.charValue();
            if (charCnt[c] == 1) //判断是否脱单了,没脱单则输出
                return c;
            else queue.remove(); //脱单了就移出队列,它不会再回来了
        }
        return '#'; //队空,返回#
    }
}

分析

字符出现次数的判断(不重复字符):
这个做法大致相同,利用 Hash 思想采用128大小的计数数组进行计数也好,或者是使用 Map 键值对映射也好,都差不多,使用数组会更简单。

字符出现顺序的判断(第一个字符):
这里就是改进的关键之处了,容易发现,字符流中不重复的字符可能同时存在多个,我们只要把这些 “不重复字符” 保存起来就可以,而无需保存那些重复出现的字符,而为了维护字符出现的顺序,我们使用队列(先进先出)这一结构,先出现的不重复字符先输出:

  • 入队:获取字符流中的一个字符时,当我们判断它是不重复时,将它加入队列;
  • 输出/出队:注意,因为队列中存储的 “不重复字符” 在一系列的流读取操作后,随时有可能改变状态(变重复),所以,队列中的字符不能直接输出,要先进行一次重复判断,如果发现队头字符已经重复了,就将它移出队列并判断新的队头,否则,输出队头的值;

arr[ch]和arr[(int)ch]是一样的

JZ67 剪绳子

思路代码(动态规划)

f(n) = max{f(n-i) × f(i)}, 0 < i < n

因为自下而上的时间复杂度为O(n), 每次递推时要对i循环O(n) ,所以时间复杂度是O(n2)

我们对长度为8的绳子进行模拟。

f(4) = f(2) * f(2) = 4;

f(5) = f(2) * f(3) = 6;

f(6) = f(3) * f(3) = 9;

f(7) = f(3) * f(4) = f(2) * f(5) = 12;

f(8) = f(3) * f(5) = 18;

所有的最优解都保存在dp[ target+1 ]

public class Solution {
    public int cutRope(int target) {
        int max=-10000;
        int[] dp =new int[target+1];
        if(target==1){
            return 1;
        }
        if(target==2){
            return 2;
        }
        if(target==3){
            return 3;
        }
        dp[1]=1;
        dp[2]=2;
        dp[3]=3;
        for(int i=4;i<=target;i++){
            for(int j=1;j<=i/2;j++){			
            //对于长度为i的绳子,计算所有可能的切分,找到最大值
			//i/2是避免重复计算,比如2*3和3*2是一样的
                int sum=dp[j]*dp[i-j];
                max = max>sum ? max:sum;
                dp[i]=max;
                
            }
        }
        return   max;
    }
}

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号