基础算法记录(二)——部分常见算法问题

本文探讨了股票交易中的几种经典算法问题,包括买卖股票的最佳时机I、II、III,涉及贪心策略和动态规划。同时介绍了寻找数组中只出现一次的数字、最长公共前缀、数数并说序列的规律,以及简单的广度优先遍历和深度优先搜索(DFS)算法。此外,还讲解了KMP字符串匹配算法和寻找数组中差值为k的数对的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

买卖股票的最佳时机 II

假设有一个数组,它的第 i 个元素是一个给定的股票在第 i 天的价格。设计一个算法来找到最大的利润。你可以完成尽可能多的交易(多次买卖股票)。然而,你不能同时参与多个交易(你必须在再次购买前出售股票)。

思路:可以采用贪心法,只要明天的价格高于今天,那么就今天买入,明天卖出,否则今天就不买入。然后继续看后天和明天的情况,以此类推

class Solution {
public static void main(String [] args){
    Solution s = new Solution();
    Scanner sc = new Scanner(System.in);
    int count = sc.nextInt();
    int [] nums = new int[count];
    for(int k=0;k<nums.length;k++){
        nums[k] = sc.nextInt();
    }
    sc.close();
    int rs = s.maxProfit(nums);
    System.out.println(rs);
}
public int maxProfit(int[] prices) {
    int i=0;
    int j=i+1;
    int max =0;
    while(j<prices.length){
        if(prices[i]<prices[j]){
            max += prices[j]-prices[i];
        }
        i=j;
        j++;
    }
    return max;
  }  
}

买卖股票的最佳时机 I

假设你有一个数组,其中第 i 个元素是一支给定股票第 i 天的价格。如果您只能完成最多一笔交易(即买入和卖出一股股票),则设计一个算法来找到最大的利润。

思路:股票某天买入和某天卖出所得的利润其实就是每相邻两天之间价格差值之和,那么我们就是要求价格差值之和最大的那部分,即求最大相邻子序列的和,问题转化为求数组中最大连续子序列和

假设dp[i]为以i结尾最大连续子序列的和,那么dp[i]=max{dp[i-1]+array[i],array[i]},最后dp[i]即最大利润。

买卖股票的最佳时机 III

如果只能限制两次,那么可以从i=第一天开始到最后一天遍历,左右两边分别动态规划,找出两边和的最大值,和只有一次的进行比较

只出现一次的数字

给定一个整数数组,除了某个元素外其余元素均出现两次。请找出这个只出现一次的元素。

异或相同为0,0异或其他数为该数本身

class Solution {
public static void main(String [] args){
     Solution s= new Solution();
     Scanner sc= new Scanner(System.in);
     int count = sc.nextInt();
     int [] nums = new int[count];
     for(int i=0;i<count;i++){
         nums[i] = sc.nextInt();
     }
    sc.close();
    int rs = s.singleNumber(nums);
    System.out.println(rs);
}

public int singleNumber(int[] nums) {
    int num=0;
    for(int i=0;i<nums.length;i++){
        num ^=nums[i];
    }
    return num;
}
}

最长公共前缀

编写一个函数来查找字符串数组中最长的公共前缀字符串

思路:只要以第一个字符串为模板判断是否是第二个的子串,如果不是就将它长度从尾减少一位,继续判断直到找到子串或者匹配不到

public class Solution {
 public static void main(String [] args) {
     Solution test = new Solution();
     Scanner sc = new Scanner(System.in);
     int count = sc.nextInt();
     sc.nextLine();
     String[] strs = new String[count];
     for(int j=0;j<count;j++) {
         strs[j] = sc.nextLine();
     }
     sc.close();
     String rs = test.longestCommonPrefix(strs);
     System.out.println(rs);
 }

 public String longestCommonPrefix(String[] strs) {
      String pre = strs[0];
      for(int i=0;i<strs.length;i++) {
          if(pre.length()==0) {
              break;
          }
          while(strs[i].indexOf(pre)!=0) {
              pre = pre.substring(0,pre.length()-1);
              if(pre.length()==0) break;
          }
      }
      return pre;
 }
}

数数并说

数数并说序列是一个整数序列,第二项起每一项的值为对前一项的计数,其前五项如下:

  1. 1
  2. 11
  3. 21
  4. 1211
  5. 111221

下一项遍历前一项进行判断就可以了,要注意代码细节问题

class Solution {
public static void main(String []args) {
    Solution  test = new Solution ();
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    sc.close();
    String rs = test.countAndSay(n);
    System.out.println(rs);
}

public String countAndSay(int n) {
    List<Integer> current = new ArrayList<>();
    List<Integer> last = new ArrayList<>();
    current.add(1);
    last.add(1);
    if(n==1) {
        return current.toString().replaceAll("\\pP|\\s*","");
    }else {
        for(int i=2;i<=n;i++) {
            int k=0;
            int count =1;
            //此处不能调用clear,因为和last同一块堆内存,会清空前面last保存的内容
            current = new ArrayList<>();
            while(k<last.size()) {
                if(k==last.size()-1) {
                    break;
                }else {

                    if(last.get(k).equals(last.get(k+1))) {
                        count++;
                        k++;
                    }else {
                        current.add(count);
                        current.add(last.get(k));
                        count =1;
                        k++;
                    }
                }
            }
            current.add(count);
            current.add(last.get(k));
            last = current;
        }

        return current.toString().replaceAll("\\pP|\\s*", "");
    }
}
}

简单的广度优先遍历

广度优先搜素如果是图,则要记录每个节点的访问状态,通过队列实现广度优先,如果每一层的元素都搜素不到满足的解,则入队列准备下一层的搜素,若搜索到,则停止搜素

public class BFS {
int [] node = {1,2,3,4,5,6};
int [] visted= new int[6];
int [][] v = {
        {0,1,1,1,0,0},
        {1,0,0,0,1,0},
        {1,0,0,0,0,1},
        {1,0,0,0,1,1},
        {0,1,0,1,0,0},
        {0,0,1,1,0,0}
        };
public static void main(String [] args) {
    BFS bfs = new BFS();
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    bfs.visted[n-1]=1;
    bfs.searchBFS(n);
}
public void searchBFS(int n) {
    Queue<Integer> queue = new LinkedList<>();
    visted[n-1]=1;
    queue.offer(node[n-1]);

    while(!queue.isEmpty()) {
        int number = queue.poll();
        System.out.println(number);
        for(int i=0;i<6;i++) {
            if(v[i][number-1]==1) {
                if(visted[i]!=1) {
                    queue.offer(node[i]);
                    visted[i]=1;
                }
            }
        }
    }

    }
}

DFS

深度优先搜素从节点的第一个分支开始搜素,继续递归搜素直到完成或超出深度

   public class DFS {
   int [] node = {1,2,3,4,5,6};
     int [] visted= new int[6];
     int [][] v = {
            {0,1,1,1,0,0},
            {1,0,0,0,1,0},
            {1,0,0,0,0,1},
            {1,0,0,0,1,1},
            {0,1,0,1,0,0},
            {0,0,1,1,0,0}
            };
    public static void main(String [] args) {
        DFS dfs = new DFS();
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        dfs.searchDFS(n);
    }
  public void searchDFS(int n) {
      if(visted[n-1]==0) {
          visted[n-1]=1;
          System.out.println(node[n-1]);
      }else if(visted[n-1]==1){
          return;
      }
      for(int i=0;i<6;i++) {
          if(v[i][n-1]==1) {
              searchDFS(i+1);
          }
      }

  }
}

KMP算法

字符串字串的寻找

关于KMP算法,可以参考http://www.cnblogs.com/yjiyjige/p/3263858.html

import java.util.Scanner;

class TestKmp {
int [] next;
public static void main(String [] args){
    TestKmp s = new TestKmp();
    Scanner sc = new Scanner(System.in);
    String str1 = sc.nextLine();
    String str2 = sc.nextLine();
    sc.close();
    s.getNext(str2.toCharArray());
    int rs = s.strStr(str1,str2);
    System.out.println(rs);
}
//kmp
public int strStr(String haystack, String needle) {
    int i=0;
    int j=0;
    char [] hay = haystack.toCharArray();
    char [] need = needle.toCharArray();


    while(i<hay.length && j<need.length){
        if(j==-1||hay[i]==need[j]){
            i++;
            j++;
        }else{
            j=next[j];
        }
    }
  //注意j==length
    if(j==need.length){
        return i-j;
    }else{
        return -1;
    }
}

public void getNext(char [] child){
    next = new int[child.length];
    next[0]=-1;
    int k=-1;
    int m=0;
  //注意数组溢出问题,每次都是赋值当前索引所在后面的元素
    while(m<child.length-1){
        if(k==-1||child[k]==child[m]){
            k++;
            m++;
            next[m] = k;
        }else{
            k=next[k];
        }
    }
    for(int l=0;l<next.length;l++) System.out.println("next[j]:"+next[l]);
}

}

从数组中找出差值为k的数对的数量,不包括重复组

比如: 1 5 3 3 1 result=2 {1,3} {3,5}

思路:先将数组排序,再将前面的数和后面比较,相等退出本层循环,继续拿第二个数和后面比较,注意如果第二个数比较前发现与第一个相等,则没有继续比较的必要,跳过本次循环,继续下一个数与后面的比较

public class Main {
public static void main(String []args) {
    Scanner sc = new Scanner(System.in);
    int n = sc.nextInt();
    int k = sc.nextInt();
    int [] nums = new int[n];
    for(int m=0;m<n;m++) {
        nums[m] = sc.nextInt();
    }
    sc.close();

    Arrays.sort(nums);

    int sum = 0;
    int i=0;
    while(i<n-1){
        if(i>0&&nums[i]==nums[i-1]) {
            i++;
            continue;
        }
        for(int j=i+1;j<n;j++) {
            if((nums[j]-nums[i])==k){
                sum++;
                i++;
                break;
            }
        }
    }

    System.out.println(sum);

}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值