Android学习总结之算法篇五(字符串)

打印 上一主题 下一主题

主题 1733|帖子 1733|积分 5199

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
字符串求回笔墨串数目

  1. public class CountPalindromicSubstrings {
  2.     /**
  3.      * 此方法用于计算字符串中回文子串的数量
  4.      * @param s 输入的字符串
  5.      * @return 回文子串的数量
  6.      */
  7.     public static int countSubstrings(String s) {
  8.         // 若输入字符串为空或长度为 0,直接返回 0
  9.         if (s == null || s.length() == 0) {
  10.             return 0;
  11.         }
  12.         // 用于记录回文子串的数量
  13.         int count = 0;
  14.         // 获取字符串的长度
  15.         int n = s.length();
  16.         // 遍历字符串中的每个字符
  17.         for (int i = 0; i < n; i++) {
  18.             // 以单个字符为中心进行扩展,统计以该字符为中心的回文子串数量
  19.             count += expandAroundCenter(s, i, i);
  20.             // 以两个相邻字符为中心进行扩展,统计以这两个相邻字符为中心的回文子串数量
  21.             count += expandAroundCenter(s, i, i + 1);
  22.         }
  23.         return count;
  24.     }
  25.     /**
  26.      * 以给定的左右索引为中心向两边扩展,计算以该中心的回文子串数量
  27.      * @param s 输入的字符串
  28.      * @param left 左索引
  29.      * @param right 右索引
  30.      * @return 以该中心的回文子串数量
  31.      */
  32.     private static int expandAroundCenter(String s, int left, int right) {
  33.         // 用于记录以该中心的回文子串数量
  34.         int count = 0;
  35.         // 当左右索引在字符串范围内,并且对应字符相等时,继续扩展
  36.         while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
  37.             // 每找到一个回文子串,数量加 1
  38.             count++;
  39.             // 左索引向左移动一位
  40.             left--;
  41.             // 右索引向右移动一位
  42.             right++;
  43.         }
  44.         return count;
  45.     }
  46.     public static void main(String[] args) {
  47.         // 定义一个示例字符串
  48.         String s = "abc";
  49.         // 调用 countSubstrings 方法计算回文子串的数量,并输出结果
  50.         System.out.println("回文子串的数目是: " + countSubstrings(s));
  51.     }
  52. }   
复制代码
字符串转整数(atoi)

题目:输入一个表示整数的字符串,转换成整数并输出。需处理正负号、溢出(返回 INT_MAX 或 INT_MIN)、非法字符(遇到非数字字符停止转换)。
  1. // 该类用于将字符串转换为整数,模拟实现 Java 中的字符串转整数功能
  2. public class StringToInteger {
  3.     // 此方法将输入的字符串 s 转换为整数
  4.     public int myAtoi(String s) {
  5.         // 获取输入字符串的长度
  6.         int len = s.length();
  7.         // 若字符串长度为 0,即空字符串,直接返回 0
  8.         if (len == 0) return 0;
  9.         // 步骤 1: 去除字符串开头的空白字符
  10.         // 初始化索引,用于遍历字符串
  11.         int index = 0;
  12.         // 当索引未越界且当前字符为空白字符时,索引向后移动
  13.         while (index < len && s.charAt(index) == ' ') {
  14.             index++;
  15.         }
  16.         // 步骤 2: 处理字符串中的符号位
  17.         // 初始化符号,默认为正号
  18.         int sign = 1;
  19.         // 若索引未越界且当前字符为正号或负号
  20.         if (index < len && (s.charAt(index) == '+' || s.charAt(index) == '-')) {
  21.             // 若为负号,将符号设为 -1;若为正号,符号保持为 1
  22.             sign = (s.charAt(index) == '-') ? -1 : 1;
  23.             // 索引向后移动一位
  24.             index++;
  25.         }
  26.         // 步骤 3: 把字符串中的数字部分转换为整数,同时处理可能的溢出情况
  27.         // 用 long 类型存储结果,避免在转换过程中出现整数溢出
  28.         long result = 0;
  29.         // 当索引未越界且当前字符为数字时
  30.         while (index < len && Character.isDigit(s.charAt(index))) {
  31.             // 把当前字符转换为对应的数字
  32.             int digit = s.charAt(index) - '0';
  33.             // 把当前数字添加到结果中
  34.             result = result * 10 + digit;
  35.             // 检查是否发生正溢出
  36.             if (sign == 1 && result > Integer.MAX_VALUE) {
  37.                 // 若发生正溢出,返回整数的最大值
  38.                 return Integer.MAX_VALUE;
  39.             }
  40.             // 检查是否发生负溢出
  41.             if (sign == -1 && result > -(long)Integer.MIN_VALUE) {
  42.                 // 若发生负溢出,返回整数的最小值
  43.                 return Integer.MIN_VALUE;
  44.             }
  45.             // 索引向后移动一位
  46.             index++;
  47.         }
  48.         // 把最终结果乘以符号,并转换为 int 类型返回
  49.         return (int)(result * sign);
  50.     }
  51.     public static void main(String[] args) {
  52.         // 创建 StringToInteger 类的实例
  53.         StringToInteger solution = new StringToInteger();
  54.         // 测试不同的输入字符串,并打印转换后的整数结果
  55.         System.out.println(solution.myAtoi("42"));         // 输出 42
  56.         System.out.println(solution.myAtoi("-42"));        // 输出 -42
  57.         System.out.println(solution.myAtoi("4193 with words")); // 输出 4193
  58.         System.out.println(solution.myAtoi("words and 987")); // 输出 0(遇到非数字字符则停止转换)
  59.         System.out.println(solution.myAtoi("-91283472332")); // 输出 -2147483648(整数的最小值,处理了溢出情况)
  60.     }
  61. }
复制代码
字符串全排列

题目:输入一个字符串(含重复字符),打印所有字符排列(不重复输出)。
算法:回溯法,递归互换字符位置生成排列,使用Set去重(或在递归时跳过重复字符)。
  1. import java.util.ArrayList;
  2. import java.util.HashSet;
  3. import java.util.List;
  4. import java.util.Set;
  5. // 该类用于生成字符串的所有不重复排列
  6. public class StringPermutation {
  7.     // 用于存储最终生成的所有排列结果
  8.     private List<String> result = new ArrayList<>();
  9.     // 存储输入字符串转换后的字符数组
  10.     private char[] chars;
  11.     // 生成字符串 s 的所有不重复排列
  12.     public List<String> permutation(String s) {
  13.         // 将输入的字符串转换为字符数组,方便后续操作
  14.         chars = s.toCharArray();
  15.         // 调用回溯函数,从索引 0 开始生成排列
  16.         backtrack(0);
  17.         // 返回存储所有排列结果的列表
  18.         return result;
  19.     }
  20.     // 回溯函数,用于生成排列
  21.     private void backtrack(int index) {
  22.         // 当索引等于字符数组的长度时,说明已经生成了一个完整的排列
  23.         if (index == chars.length) {
  24.             // 将当前字符数组转换为字符串,并添加到结果列表中
  25.             result.add(new String(chars));
  26.             // 递归终止,返回上一层
  27.             return;
  28.         }
  29.         // 使用 Set 记录已经交换过的字符,避免生成重复的排列
  30.         Set<Character> used = new HashSet<>();
  31.         // 从当前索引开始,遍历字符数组
  32.         for (int i = index; i < chars.length; i++) {
  33.             // 如果当前字符已经在 Set 中,说明已经交换过,跳过该字符
  34.             if (used.contains(chars[i])) continue;
  35.             // 将当前字符添加到 Set 中,表示已经使用过
  36.             used.add(chars[i]);
  37.             // 交换当前索引和 i 索引位置的字符
  38.             swap(index, i);
  39.             // 递归调用回溯函数,处理下一个索引位置
  40.             backtrack(index + 1);
  41.             // 回溯操作,恢复交换前的状态,以便尝试其他排列
  42.             swap(index, i);
  43.         }
  44.     }
  45.     // 交换字符数组中两个位置的字符
  46.     private void swap(int i, int j) {
  47.         // 临时存储 i 位置的字符
  48.         char temp = chars[i];
  49.         // 将 j 位置的字符赋值给 i 位置
  50.         chars[i] = chars[j];
  51.         // 将临时存储的字符赋值给 j 位置
  52.         chars[j] = temp;
  53.     }
  54.     public static void main(String[] args) {
  55.         // 创建 StringPermutation 类的实例
  56.         StringPermutation solution = new StringPermutation();
  57.         // 测试输入字符串 "abc",并打印生成的所有排列
  58.         System.out.println(solution.permutation("abc"));
  59.         // 测试输入字符串 "aab",并打印生成的所有不重复排列
  60.         System.out.println(solution.permutation("aab"));
  61.     }
  62. }
复制代码
复杂度分析


  • 时间复杂度:O (n×n!),n 为字符串长度。每个位置有 n, n-1, ..., 1 种可能,统共有 n! 种排列,每次生成排列需 O (n) 时间复制字符数组。
  • 空间复杂度:O (n),递归栈深度为 n,存储排列的空间为 n!×n(均匀环境可视为 O (n!))。
找最小的 k 个数

题目:从 n 个数中找到最小的 k 个数(k≤n)。
算法:使用最大堆(优先队列)维护 k 个元素,堆顶是当前 k 个数中的最大值。遍历数组时,若当前数小于堆顶,则替换堆顶,包管堆中始终是最小的 k 个数。
  1. import java.util.ArrayList;
  2. import java.util.List;
  3. import java.util.PriorityQueue;
  4. // 该类用于找出数组中最小的 k 个数
  5. public class SmallestKNumbers {
  6.     // 该方法用于从数组 arr 中找出最小的 k 个数
  7.     public List<Integer> getLeastNumbers(int[] arr, int k) {
  8.         // 用于存储最终找到的最小的 k 个数
  9.         List<Integer> result = new ArrayList<>();
  10.         // 如果 k 为 0,说明不需要找最小的数,直接返回空列表
  11.         if (k == 0) return result;
  12.         // 使用优先队列来实现最大堆。
  13.         // PriorityQueue 默认是最小堆,通过传入 (a, b) -> b - a 这个逆序比较器,将其转换为最大堆
  14.         PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
  15.         // 遍历数组中的每个元素
  16.         for (int num : arr) {
  17.             // 如果最大堆中的元素数量小于 k,直接将当前元素添加到最大堆中
  18.             if (maxHeap.size() < k) {
  19.                 maxHeap.offer(num);
  20.             } else if (num < maxHeap.peek()) {
  21.                 // 如果最大堆已经有 k 个元素,且当前元素比堆顶元素(堆中的最大值)小
  22.                 // 则移除堆顶元素,将当前元素添加到堆中
  23.                 maxHeap.poll();
  24.                 maxHeap.offer(num);
  25.             }
  26.         }
  27.         // 遍历最大堆,将堆中的元素依次添加到结果列表中
  28.         while (!maxHeap.isEmpty()) {
  29.             result.add(maxHeap.poll());
  30.         }
  31.         // 返回存储最小的 k 个数的列表
  32.         return result;
  33.     }
  34.     public static void main(String[] args) {
  35.         // 创建 SmallestKNumbers 类的实例
  36.         SmallestKNumbers solution = new SmallestKNumbers();
  37.         // 定义一个测试数组
  38.         int[] arr = {3, 2, 1, 5, 6, 4};
  39.         // 调用 getLeastNumbers 方法找出数组中最小的 2 个数,并打印结果
  40.         System.out.println(solution.getLeastNumbers(arr, 2));
  41.         // 结果可能是 [1, 2],由于堆结构不保证元素的顺序,实际输出顺序可能不同,可后续排序
  42.     }
  43. }
复制代码
优化说明


  • 若需要效果排序,可在最后对列表排序:result.sort((a,b)->a-b);。
  • 复杂度分析

    • 时间复杂度:O (n log k),每个元素入堆和出堆操作均为 O (log k),共 n 次操作。
    • 空间复杂度:O (k),堆中存储 k 个元素。

其他方法对比


  • 排序法:先排序数组,取前 k 个。时间复杂度 O (n log n),实用于 n 较小的环境。
  • 快速选择(雷同快排分区):均匀时间复杂度 O (n),但最坏环境 O (n²),且不实用于海量数据(需修改原数组)。
环形链表总集

  1. // 定义链表节点类,每个节点包含一个整数值和一个指向下一个节点的引用
  2. class ListNode {
  3.     int val;
  4.     ListNode next;
  5.     // 构造函数,用于初始化节点的值
  6.     ListNode(int x) {
  7.         val = x;
  8.         next = null;
  9.     }
  10. }
  11. public class CircularLinkedListAlgorithms {
  12.     /**
  13.      * 判断链表是否有环
  14.      * @param head 链表的头节点
  15.      * @return 如果链表有环返回 true,否则返回 false
  16.      */
  17.     public static boolean hasCycle(ListNode head) {
  18.         // 若链表为空或者只有一个节点,不可能存在环,直接返回 false
  19.         if (head == null || head.next == null) {
  20.             return false;
  21.         }
  22.         // 慢指针,每次移动一步
  23.         ListNode slow = head;
  24.         // 快指针,每次移动两步
  25.         ListNode fast = head;
  26.         // 当快指针和其下一个节点都不为空时,继续循环
  27.         while (fast != null && fast.next != null) {
  28.             // 慢指针移动一步
  29.             slow = slow.next;
  30.             // 快指针移动两步
  31.             fast = fast.next.next;
  32.             // 如果慢指针和快指针相遇,说明链表存在环
  33.             if (slow == fast) {
  34.                 return true;
  35.             }
  36.         }
  37.         // 快指针到达链表末尾,说明链表不存在环
  38.         return false;
  39.     }
  40.     /**
  41.      * 找到环形链表的入环点
  42.      * @param head 链表的头节点
  43.      * @return 入环点的节点,如果链表无环则返回 null
  44.      */
  45.     public static ListNode detectCycle(ListNode head) {
  46.         // 若链表为空或者只有一个节点,不可能存在环,直接返回 null
  47.         if (head == null || head.next == null) {
  48.             return null;
  49.         }
  50.         // 慢指针,每次移动一步
  51.         ListNode slow = head;
  52.         // 快指针,每次移动两步
  53.         ListNode fast = head;
  54.         // 标记链表是否有环
  55.         boolean hasCycle = false;
  56.         // 当快指针和其下一个节点都不为空时,继续循环
  57.         while (fast != null && fast.next != null) {
  58.             // 慢指针移动一步
  59.             slow = slow.next;
  60.             // 快指针移动两步
  61.             fast = fast.next.next;
  62.             // 如果慢指针和快指针相遇,说明链表存在环
  63.             if (slow == fast) {
  64.                 hasCycle = true;
  65.                 break;
  66.             }
  67.         }
  68.         // 如果链表无环,返回 null
  69.         if (!hasCycle) {
  70.             return null;
  71.         }
  72.         // 慢指针重新指向头节点
  73.         slow = head;
  74.         // 慢指针和快指针同时移动一步,直到它们相遇,相遇点即为入环点
  75.         while (slow != fast) {
  76.             slow = slow.next;
  77.             fast = fast.next;
  78.         }
  79.         return slow;
  80.     }
  81.     /**
  82.      * 计算环形链表的环长度
  83.      * @param head 链表的头节点
  84.      * @return 环的长度,如果链表无环则返回 0
  85.      */
  86.     public static int cycleLength(ListNode head) {
  87.         // 若链表为空或者只有一个节点,不可能存在环,直接返回 0
  88.         if (head == null || head.next == null) {
  89.             return 0;
  90.         }
  91.         // 慢指针,每次移动一步
  92.         ListNode slow = head;
  93.         // 快指针,每次移动两步
  94.         ListNode fast = head;
  95.         // 标记链表是否有环
  96.         boolean hasCycle = false;
  97.         // 当快指针和其下一个节点都不为空时,继续循环
  98.         while (fast != null && fast.next != null) {
  99.             // 慢指针移动一步
  100.             slow = slow.next;
  101.             // 快指针移动两步
  102.             fast = fast.next.next;
  103.             // 如果慢指针和快指针相遇,说明链表存在环
  104.             if (slow == fast) {
  105.                 hasCycle = true;
  106.                 break;
  107.             }
  108.         }
  109.         // 如果链表无环,返回 0
  110.         if (!hasCycle) {
  111.             return 0;
  112.         }
  113.         // 初始化环的长度为 1
  114.         int length = 1;
  115.         // 快指针移动一步
  116.         fast = fast.next;
  117.         // 快指针继续移动,直到再次和慢指针相遇,记录移动的步数
  118.         while (slow != fast) {
  119.             fast = fast.next;
  120.             length++;
  121.         }
  122.         return length;
  123.     }
  124.     public static void main(String[] args) {
  125.         // 构建一个有环的链表示例
  126.         ListNode node1 = new ListNode(1);
  127.         ListNode node2 = new ListNode(2);
  128.         ListNode node3 = new ListNode(3);
  129.         ListNode node4 = new ListNode(4);
  130.         node1.next = node2;
  131.         node2.next = node3;
  132.         node3.next = node4;
  133.         node4.next = node2; // 形成环
  134.         // 调用 hasCycle 方法判断链表是否有环并输出结果
  135.         System.out.println("链表是否有环: " + hasCycle(node1));
  136.         // 调用 detectCycle 方法找到入环点
  137.         ListNode entryPoint = detectCycle(node1);
  138.         if (entryPoint != null) {
  139.             // 若有入环点,输出入环点的值
  140.             System.out.println("入环点的值: " + entryPoint.val);
  141.         } else {
  142.             // 若没有入环点,输出提示信息
  143.             System.out.println("没有入环点");
  144.         }
  145.         // 调用 cycleLength 方法计算环的长度并输出结果
  146.         System.out.println("环的长度: " + cycleLength(node1));
  147.     }
  148. }   
复制代码
有序数组去重

  1. public class RemoveDuplicatesSortedArray {
  2.     /**
  3.      * 此方法用于移除有序数组中的重复元素,使每个元素只出现一次。
  4.      * 并返回移除重复元素后数组的新长度。
  5.      * 原数组会被修改,新长度之前的元素为去重后的元素。
  6.      *
  7.      * @param nums 输入的有序整数数组
  8.      * @return 去重后数组的新长度
  9.      */
  10.     public static int removeDuplicates(int[] nums) {
  11.         // 如果数组为空,直接返回 0
  12.         if (nums == null || nums.length == 0) {
  13.             return 0;
  14.         }
  15.         // 慢指针,指向去重后数组的最后一个位置
  16.         int slow = 0;
  17.         // 快指针,用于遍历数组
  18.         for (int fast = 1; fast < nums.length; fast++) {
  19.             // 如果快指针指向的元素和慢指针指向的元素不相等
  20.             if (nums[fast] != nums[slow]) {
  21.                 // 慢指针向后移动一位
  22.                 slow++;
  23.                 // 将快指针指向的元素赋值给慢指针当前位置
  24.                 nums[slow] = nums[fast];
  25.             }
  26.         }
  27.         // 慢指针的位置加 1 就是去重后数组的长度
  28.         return slow + 1;
  29.     }
  30.     public static void main(String[] args) {
  31.         // 定义一个有序数组
  32.         int[] nums = {1, 1, 2, 2, 3, 4, 4, 4, 5};
  33.         // 调用 removeDuplicates 方法进行去重
  34.         int newLength = removeDuplicates(nums);
  35.         System.out.println("去重后数组的新长度: " + newLength);
  36.         System.out.print("去重后的数组元素: ");
  37.         for (int i = 0; i < newLength; i++) {
  38.             System.out.print(nums[i] + " ");
  39.         }
  40.     }
  41. }   
复制代码
数组归并区间 

  1. import java.util.ArrayList;
  2. import java.util.Arrays;
  3. import java.util.Comparator;
  4. import java.util.List;
  5. public class MergeIntervals {
  6.     /**
  7.      * 合并重叠的区间
  8.      * @param intervals 输入的区间数组
  9.      * @return 合并后不重叠的区间数组
  10.      */
  11.     public static int[][] merge(int[][] intervals) {
  12.         // 如果输入数组为空或者长度为 0,直接返回空数组
  13.         if (intervals == null || intervals.length == 0) {
  14.             return new int[0][0];
  15.         }
  16.         // 按照区间的起始位置进行排序
  17.         Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));
  18.         // 用于存储合并后的区间
  19.         List<int[]> merged = new ArrayList<>();
  20.         // 取第一个区间作为初始的合并区间
  21.         int[] current = intervals[0];
  22.         // 遍历剩余的区间
  23.         for (int i = 1; i < intervals.length; i++) {
  24.             int[] interval = intervals[i];
  25.             // 如果当前区间的结束位置大于等于下一个区间的起始位置,说明有重叠
  26.             if (current[1] >= interval[0]) {
  27.                 // 更新当前区间的结束位置为两个区间结束位置的最大值
  28.                 current[1] = Math.max(current[1], interval[1]);
  29.             } else {
  30.                 // 没有重叠,将当前区间加入到合并列表中
  31.                 merged.add(current);
  32.                 // 更新当前区间为下一个区间
  33.                 current = interval;
  34.             }
  35.         }
  36.         // 将最后一个合并的区间加入到列表中
  37.         merged.add(current);
  38.         // 将列表转换为二维数组并返回
  39.         return merged.toArray(new int[merged.size()][]);
  40.     }
  41.     public static void main(String[] args) {
  42.         int[][] intervals = {{1, 3}, {2, 6}, {8, 10}, {15, 18}};
  43.         int[][] mergedIntervals = merge(intervals);
  44.         // 输出合并后的区间
  45.         for (int[] interval : mergedIntervals) {
  46.             System.out.println(Arrays.toString(interval));
  47.         }
  48.     }
  49. }   
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天空闲话

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表