九章算法第七课,两根指针&TwoSum问题

这篇博客介绍了多个数组算法问题的解决方案,如两数之和、奇偶分割数组、颜色分类等,强调了指针在解决问题中的应用,以及如何处理数组的边界条件和移动条件。还提及了数据结构的选择,如哈希表在处理重复元素时的重要性。

539. 移动零

class Solution {
public:
    void moveZeroes(vector<int> &nums) {
       if (nums.size()==0) { return; }
       //找到第一个是0的位置;
       int i;
       for (i=0;i<nums.size();i++) {
           if (nums[i]==0) break;
       }
       
       int pos=i; //pos记录下一个不是0的数要换到的位置
       //不管pos上是不是0,只要后面有非0就往前换
       for (i;i<nums.size();i++) {
           if (nums[i]!=0) {
               swap(nums[i],nums[pos]);
               pos++;
           }
       }
    }
};

415. 有效回文串
注意两个内置函数的用法:
transform(字符串头,字符串尾,新字符串头,::tolower||::toupper)
isalnum(char)

class Solution {
public:
    bool isPalindrome(string &s) {
        if (s.length()==0 || s.length()==1) return true;
        transform(s.begin(),s.end(),s.begin(),::tolower);
        
        int i=0; int j=s.length()-1;
        while (i<j) {
            while (!valid(s[i])) i++; //可以用::isalnum(s[i])替代
            while (!valid(s[j])) j--;
            if (i<j && s[i]!=s[j]) return false;
            i++;
            j--;
        }
    }
    bool valid(char c){
        if (c>='0' && c<='9' || c>='a' && c<='z')
            return true;
        else return false;
    }
};

31. 数组划分
注意最后i和j的位置需要判断一下;

class Solution {
public:
    int partitionArray(vector<int> &nums, int k) {
        if (nums.size()==0) return 0;
        
        int i=0;
        int j=nums.size()-1;
        while (i<j) {
            while (i<j && nums[i]<k) i++;
            while (i<j && nums[j]>=k) j--;
            if (i<j) {
                swap(nums[i],nums[j]);
                i++;
                j--;
            }
        }
        if (i==j) return nums[i]<k?i+1:i;
        else return j+1;
    }
};

?461. 无序数组K小元素
思想类似于快排,大体分好左右两部分,确定第k个数在左边还是在右边。由于只需要排一边,时间复杂度O(n)。
注意分割完了之后挑选两部分怎么选。一定要把i,j所在的数包含进去还不能出现划分完了和之前规模一样的情况。
所以需要循环条件设置为(i<=j),移动条件设置为i>pivot和j<pivot;使得ij重合出现在边界的情况不可能出现。

class Solution {
public:
    int kthSmallest(int k, vector<int> &nums) {
        return helper(nums,0,nums.size()-1,k);
    }
    int helper(vector<int>&nums,int start,int end,int k) {
        if (start==end) return nums[start];
        int pivot=nums[(start+end)/2];
        
        int i=start;
        int j=end;
        //要有等号,让i和j交叠或错开,不然可能会出现永远分不开的情况。
        while (i<=j) {
            while (i<=j && nums[i]<pivot) i++;
            while (i<=j && nums[j]>pivot) j--;
            if (i<=j) {
                swap(nums[i],nums[j]);
                i++;
                j--;
            }
        }
        //i,j的位置有两种可能,一种是错开,一种是重合,分情况讨论。
        //不管是错开还是重合,都要保证右边的边界进右边部分,左边的部分进左边部分。
        //如果左右都不是,说明j和i错开了,中间那个就是要找的值。
        if (k<=j-start+1) return helper(nums,start,j,k);
        if (k>=i-start+1) return helper(nums,i,end,k-(i-start));
        return nums[j+1];
    }
};

373. 奇偶分割数组
也是利用快排思想,但是不涉及到二次划分数组,所以不需要考虑会不会分不开的问题。

class Solution {
public:
    void partitionArray(vector<int> &nums) {
        if (nums.size()==0 || nums.size()==1) return;
        
        int i=0;
        int j=nums.size()-1;
        while (i<j) {
            while (i<j && nums[i]%2==1) i++;
            while (i<j && nums[j]%2==0) j--;
            if (i<j) {
                swap(nums[i],nums[j]);
                i++;
                j--;
            }
        }
    }
};

144. 交错正负数
第一步,将数组分为前正后负两部分;第二步,将其穿插;
注意正负数的个数不一定一样,需要分情况讨论。

class Solution {
public:
    void rerange(vector<int> &A) {
        if (A.size()==0 || A.size()==1) return;
        
        int i=0;
        int j=A.size()-1;
        while (i<j) {
            while (i<j && A[i]<0) i++;
            while (i<j && A[j]>0) j--;
            if (i<j) {
                swap(A[i],A[j]);
                i++;
                j--;
            }
        }
        //可以证明i一定是正数
            //负数多
            if (i>A.size()-i) interleave(A,1,A.size()-1);
            //正数多
            else if (i<A.size()-i) interleave(A,0,A.size()-2);
            //一样多
            else interleave(A,1,A.size()-2);
    }
    void interleave(vector<int> &A,int start,int end) {
        while (start<end) {
            swap(A[start],A[end]);
            start=start+2;
            end=end-2;
        }
    }
};


49. 字符大小写排序

class Solution {
public:
    void sortLetters(string &letters) {
        int i = 0, j = letters.size() - 1;
        while (i<j) {
            while (i<j && isLower(letters[i])) i++;
            while (i<j && !isLower(letters[j])) j--;
            if (i<j) {
                swap(letters[i],letters[j]);
                i++;
                j--;
            }
        }
    }
    bool isLower(char c) {
        return c >= 'a' && c <= 'z';
    }
};

148. 颜色分类
最简单的方法是for两边遍,第一遍记录个数,第二遍赋值。如果只能for一遍则需要使用指针的算法,注意边界条件和指针移动条件。

class Solution {
public:
    void sortColors(vector<int> &nums) {
        if (nums.size()==0 || nums.size()==1) return;
        
        int i=0;//i的左边都是0
        int j=nums.size()-1;//j的右边都是2
        int k=0;//找1
        while (k<=j) {//1的下标挪到2的起始位置时结束,要有等号,因为j可能还没处理
            if (nums[k]==0) {
                swap(nums[i],nums[k]);
                i++;
                //注意这里直接k++,不担心换过来的数字需要再换,因为i在k之前必定已经处理完了
                k++;
            }
            else if (nums[k]==1) k++;
            else if (nums[k]==2) {
                swap(nums[k],nums[j]);
                j--;
                //这里不可以k++,因为不知道换过来的是个啥
            }
        }
    }
};

143. 排颜色 II

class Solution {
public:
    void sortColors2(vector<int> &colors, int k) {
        if (colors.size()==0 ||colors.size()==1) return;

        sort(colors,0,colors.size()-1,1,k);
    }
    void sort(vector<int> &colors,int start,int end, 
              int colorsFrom, int colorsTo) {
        if (start>=end || colorsTo==colorsFrom) return;
        
        
        int mid=(colorsTo+colorsFrom)/2;
        int i=start;
        int j=end;
        //必须缩小范围,ij需要错开,所以加上=
        while (i<=j) {
            //由于mid是取上整,为了避免i左边没有数,应该把等于mid的情况归类到左边;
            while (i<=j && colors[i]<=mid) i++;
            while (i<=j && colors[j]>mid) j--;
            if (i<=j) {
                swap(colors[i],colors[j]);
                i++;
                j--;
            }
        }
        //排完之后j在左边,i在右边,需要缩小范围
        sort(colors,start,j,colorsFrom,mid);
        sort(colors,i,end,mid+1,colorsTo);
    }
};

烙饼排序
烙饼排序最简单的想法类似汉诺塔,先排好最下面的,然后排倒数第二个...最少操作两次就可以排好一个位置(假设要排第n个饼,饼位于x,则先将x以上的饼全部反转,x反转到最上面,然后再将n个饼反转)。加上最后两个饼只需要一次就能排好位置,因此最多2n-3次即可排好所有烙饼。
如果想减少反转次数找到最优解,只能通过搜索+剪枝的方法,动归等被证明不可以。
搜索即搜索所有可能的操作,记录达到有序一共需要的次数,但是时间复杂度会变的很高,因此需要剪枝。
剪枝即寻找提前出口的条件:
1.操作已超过2n-3次还没排好序;
2.已经排好序;
3.已经操作的次数+将要操作的次数大于2n-3次。由于每次操作只能使两个使一个烙饼与大小和它相邻的烙饼排到一起,因此想要有序最少的操作次数计算如下:

lowerBound = 0;
for(index = 0; index < size - 1; index ++) {
    if(cakeArray[index] - cakeArray[index+1] !=1 
    && cakeArray[index] - cakeArray[index+1] != -1)
        lowerBound++;
}

4.上次刚反转完,同一个饼,则不操作。

if(index == lastIndex) continue;

5.缓存从初始状态将到当前状态过程中的所以状态,然后搜索当前状态是否在缓存中出现过,如果出现过则退出。将之前的状态变成hashkey存储,在本状态退出之后需要删除此状态的hashkey。 


睡眠排序、面条排序、猴子排序,其中猴子排序用了随机洗牌算法,以及生成随机数

607. 两数之和 III-数据结构设计
注意考虑重复数字的问题,使用unordered_multimap。map、set都是使用红黑树实现的,而unordered_map、unordered_set才是哈希表实现的,multi表明可以有重复。

class TwoSum {
public:
    unordered_multiset<int> hash;
    void add(int number) {
        hash.insert(number);
    }
    bool find(int value) {
        for (auto n:hash) {
            if (value==2*n) {
                if (hash.count(n)>=2) return true;
            }
            else if (hash.count(value-n)>=1) return true;
        }
        return false;
    }
};

608. 两数和 II-输入已排序的数组

class Solution {
public:
    vector<int> twoSum(vector<int> &nums, int target) {
        vector<int> res;
        int i=0; int j=nums.size()-1;
        while (i<j) {
            if (nums[i]+nums[j]==target) {
                res.push_back(i+1);
                res.push_back(j+1);
                return res;
            }
            if (nums[i]+nums[j]<target) i++;
            else j--;
        }
    }
};

587. 两数之和 - 不同组成
不可以先去重!

class Solution {
public:
    int twoSum6(vector<int> &nums, int target) {
        sort(nums.begin(),nums.end());
        int res=0;
        int i=0; int j=nums.size()-1;
        while (i<j) {
            if (nums[i]+nums[j]==target) {
                res++;
                while (i<j && nums[i+1]==nums[i]) i++;
                while (i<j && nums[j-1]==nums[j]) j--;
            }
            if (nums[i]+nums[j]<target) i++;
            else j--;
        }
        return res;
    }
};

57. 三数之和
确定一个数,另外两个数用双指针。注意也要跳过重复数字的情况。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int> &numbers) {
        vector<vector<int>> res;
        sort(numbers.begin(),numbers.end());
        
        for (int i=0;i<=numbers.size()-3;i++) {
            if (i!=0 && numbers[i]==numbers[i-1]) continue;
            int target=-numbers[i];
            int left=i+1; int right=numbers.size()-1;
            while (left<right) {
                if (numbers[left]+numbers[right]==target) {
                    vector<int> tmp;
                    tmp.push_back(numbers[i]);
                    tmp.push_back(numbers[left]);
                    tmp.push_back(numbers[right]);
                    res.push_back(tmp);
                    while (left<right && numbers[left+1]==numbers[left]) left++;
                    while (left<right && numbers[right-1]==numbers[right]) right--;
                    left++; right--;
                }
                else if (numbers[left]+numbers[right]<target) left++;
                else right--;
            }
        }
        return res;
    }
};

609. 两数和-小于或等于目标值
算法也是不回头的,因为假如nums[i]+nums[j+1]>target && nums[i]+nums[j]<=target, 则nums[i+1]+nums[j+1]不可能小于等于target,j指针不必回头。

class Solution {
public:
    int twoSum5(vector<int> &nums, int target) {
        if (nums.size()<2) return 0; 
        sort(nums.begin(),nums.end());
        int res=0;
        
        int i=0;
        int j=nums.size()-1;
        while (i<j) {
            if (nums[i]+nums[j]<=target) {
                res=res+(j-i);
                i++;
            }
            if (nums[i]+nums[j]>target) j--;
        }
        return res;
    }
};

443. 两数之和 II
指针反过来移动即可。

class Solution {
public:
    int twoSum2(vector<int> &nums, int target) {
        if (nums.size()<2) return 0; 
        sort(nums.begin(),nums.end());
        int res=0;
        
        int i=0;
        int j=nums.size()-1;
        while (i<j) {
            if (nums[i]+nums[j]>target) {
                res=res+(j-i);
                j--;
            }
            if (nums[i]+nums[j]<=target) i++;
        }
        return res;
    }
};

533. 两数和的最接近值
因为i和j都是贴着最可能接近target的位置移动的,所以ij也都不回头。

class Solution {
public:
    int twoSumClosest(vector<int> &nums, int target) {
        sort(nums.begin(),nums.end());
        int res=INT_MAX;
        int i=0; int j=nums.size()-1;
        while (i<j) {
            if (nums[i]+nums[j]==target) {
                return 0;
            }
            res=min(res,abs(nums[i]+nums[j]-target));
            if (nums[i]+nums[j]<target) i++;
            else j--;
        }
        return res;
    }
};

59. 最接近的三数之和

class Solution {
public:
    int threeSumClosest(vector<int> &numbers, int target) {
        if (numbers.size()<3) return -1;
        
        sort(numbers.begin(),numbers.end());
        int res=INT_MAX;
        
        for (int i=0;i<numbers.size()-2;i++) {
            int j=i+1;
            int k=numbers.size()-1;
            while (j<k) {
                int sum=numbers[i]+numbers[j]+numbers[k];
                if (sum==target) return target;
                if (abs(sum-target)<abs(res-target)) res=sum;
                if (sum<target) j++;
                else k--;
            }
        }
        return res;
    }
};

58. 四数之和
和之前的方法一样即可。下面的代码尝试了一种使用哈希表的做法。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int> &numbers, int target) {
        sort(numbers.begin(),numbers.end());
        vector<vector<int>> res;
        if (numbers.size()<4) return res;
        unordered_map<int,vector<pair<int,int>>> twosum;
        
        //记录两数和
        for (int i=0;i<numbers.size()-1;i++)
            for (int j=i+1;j<numbers.size();j++) {
                if (twosum.find(numbers[i]+numbers[j])==twosum.end()) {
                    vector<pair<int,int>> tmp;
                    tmp.push_back(make_pair(i,j));
                    twosum[numbers[i]+numbers[j]]=tmp;
                }
                else {
                    int flag=0;
                    for (auto sum:twosum[numbers[i]+numbers[j]])
                        if (numbers[sum.first]==numbers[i] && numbers[sum.second]==numbers[j]
                           || numbers[sum.first]==numbers[j] && numbers[sum.second]==numbers[i]){
                            flag=1;
                            break;
                        }
                    if (flag==0) twosum[numbers[i]+numbers[j]].push_back(make_pair(i,j));
                }
            }

        for (int i=0;i<numbers.size()-1;i++){
            //不可用if (i!=0 && numbers[i]==numbers[i-1]) continue;
            //因为下面第一个位置的数可能不符合下面防止镜像解的条件而后面的位置可以,但是却被跳过了
            for (int j=i+1;j<numbers.size();j++) {
                //不可用 if (j!=i+1 && numbers[j]==numbers[j-1]) continue; 去重
                if (twosum.find(target-numbers[i]-numbers[j])!=twosum.end()) {
                    for (auto sum:twosum[target-numbers[i]-numbers[j]]) {
                        if (i>sum.first && i>sum.second) {//防止找到镜像解a+b+c+d和c+d+a+b
                            vector<int> tmp;
                            tmp.push_back(numbers[sum.first]);
                            tmp.push_back(numbers[sum.second]);
                            tmp.push_back(numbers[i]);
                            tmp.push_back(numbers[j]);
                            //查重
                            int flag=0;
                            for (int k=0;k<res.size();k++)
                               if (res[k]==tmp) {flag=1; break;}
                            if (flag==0) res.push_back(tmp);
                        }
                    }
                }
            }
        }
        return res;
    }
};

610. 两数和 - 差等于目标值
两数差,没说减数和被减数的关系,所以要统一用abs来判断。

class Solution {
public:
    vector<int> twoSum7(vector<int> &nums, int target) {
        vector<int> res;
        if (nums.size()==0) res;
        vector<pair<int,int>> m;
        for (int i=0;i<nums.size();i++) {
            m.push_back(make_pair(nums[i],i));
        }
        
        sort(m.begin(),m.end());
        int i=0;
        int j=1;
        while (j<m.size()) {
            if (i==j) j++;
            else if (abs(m[j].first-m[i].first)==abs(target)) {
                res.push_back(min(m[i].second+1,m[j].second+1));
                res.push_back(max(m[i].second+1,m[j].second+1));
                return res;
            }else if (abs(m[j].first-m[i].first)>abs(target)) i++;
            else j++;
        }
    }
};

 

 

好的,我将依次为您解答算法设计题,并提供详细的解析和示例代码(如有)。以下是具体的解答: ### 算法设计题 1 **题目重述** 设计一个算法,找出数组中两个数的和等于给定的目标值,并返回这两个数的索引。假设每个输入只有一个答案,并且不能重复使用同一个元素。 **详解** 我们可以使用哈希表来实现这一目标。遍历数组时,检查当前元素是否已经在哈希表中找到了配对的元素。如果没有找到,则将当前元素及其索引存入哈希表。如果找到了,则返回两个元素的索引。 **算法步骤**: 1. 初始化一个空的哈希表。 2. 遍历数组,对于每个元素 `nums[i]`,检查 `target - nums[i]` 是否存在于哈希表中。 3. 如果存在,返回当前元素和哈希表中对应的索引。 4. 如果不存在,将 `nums[i]` 和索引 `i` 存入哈希表。 5. 返回结果。 **代码概述** ```python def two_sum(nums, target): hash_table = {} for i, num in enumerate(nums): complement = target - num if complement in hash_table: return [hash_table[complement], i] hash_table[num] = i return [] ``` **知识点** 1. 哈希表:用于快速查找配对元素。 2. 数组遍历:逐个检查数组中的元素。 --- ### 算法设计题 2 **题目重述** 设计一个算法,计算斐波那契数列的第 n 项。斐波那契数列的定义为:F(0) = 0, F(1) = 1, 对于 n >= 2, F(n) = F(n-1) + F(n-2)。 **详解** 斐波那契数列可以通过多种方法计算,包括递归、带备忘录的递归(记忆化递归)以及动态规划。这里我们使用动态规划来实现,以确保时间复杂度为 O(n),并且不需要额外的空间来存储中间结果。 **算法步骤**: 1. 特殊情况处理:如果 n 是 0 或 1,直接返回 n。 2. 初始化两个变量 `a` 和 `b` 分别表示 F(n-2) 和 F(n-1)。 3. 使用循环从 2 开始计算到 n,更新 `a` 和 `b`。 4. 返回 `b`,即 F(n)。 **代码概述** ```python def fibonacci(n): if n <= 1: return n a, b = 0, 1 for _ in range(2, n + 1): a, b = b, a + b return b ``` **知识点** 1. 动态规划:用于优化递归计算。 2. 循环优化:减少不必要的递归调用。 --- ### 算法设计题 3 **题目重述** 设计一个算法,判断给定的整数链表是否为回文链表。不允许使用额外的空间。 **详解** 为了判断链表是否为回文链表,我们可以通过以下步骤: 1. 找到链表的中点。 2. 反转链表的后半部分。 3. 比较前半部分和反转后的后半部分是否相等。 4. 如果相等,则链表是回文链表;否则不是。 **算法步骤**: 1. 使用快慢指针找到链表的中点。 2. 反转链表的后半部分。 3. 同时遍历前半部分和反转后的后半部分,比较节点值。 4. 如果所有节点值都相等,则链表是回文链表。 **代码概述** ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def is_palindrome(head): if not head or not head.next: return True # 找到中点 slow, fast = head, head while fast and fast.next: slow = slow.next fast = fast.next.next # 反转后半部分 prev = None while slow: next_node = slow.next slow.next = prev prev = slow slow = next_node # 比较前半部分和反转后的后半部分 left, right = head, prev while right: if left.val != right.val: return False left = left.next right = right.next return True ``` **知识点** 1. 快慢指针:用于找到链表中点。 2. 链表反转:用于处理链表的后半部分。 --- ### 算法设计题 4 **题目重述** 设计一个算法,在未排序的数组中找到第 k 小的元素。要求时间复杂度尽可能低。 **详解** 可以使用快速选择算法(Quickselect),它是快速排序的一种变体,用于在未排序数组中找到第 k 小的元素。快速选择算法的平均时间复杂度为 O(n),最坏情况下为 O(n^2)。为了优化最坏情况,可以使用随机化策略。 **算法步骤**: 1. 选择一个枢轴元素(pivot),这里可以选择第一个元素或随机选择。 2. 将数组分为小于枢轴的部分和大于枢轴的部分。 3. 根据枢轴的位置,递归处理左侧或右侧部分,直到找到第 k 小的元素。 **代码概述** ```python import random def partition(nums, left, right): pivot = nums[left] i = left + 1 for j in range(left + 1, right + 1): if nums[j] < pivot: nums[i], nums[j] = nums[j], nums[i] i += 1 nums[left], nums[i - 1] = nums[i - 1], nums[left] return i - 1 def quick_select(nums, left, right, k): if left == right: return nums[left] pivot_index = random.randint(left, right) nums[left], nums[pivot_index] = nums[pivot_index], nums[left] pivot_index = partition(nums, left, right) if k == pivot_index: return nums[k] elif k < pivot_index: return quick_select(nums, left, pivot_index - 1, k) else: return quick_select(nums, pivot_index + 1, right, k) def find_kth_smallest(nums, k): return quick_select(nums, 0, len(nums) - 1, k - 1) ``` **知识点** 1. 快速选择算法:用于在未排序数组中找到第 k 小的元素。 2. 随机化策略:优化最坏情况性能。 --- ### 算法设计题 5 **题目重述** 设计一个算法,实现二叉树的层序遍历(广度优先搜索)。 **详解** 层序遍历也称为广度优先搜索(BFS),可以通过队列来实现。从根节点开始,将根节点加入队列,然后逐层处理节点,将每个节点的子节点依次加入队列。 **算法步骤**: 1. 初始化一个队列,将根节点加入队列。 2. 当队列不为空时,取出队首节点并处理。 3. 将当前节点的左子节点和右子节点依次加入队列。 4. 重复上述步骤,直到队列为空。 **代码概述** ```python from collections import deque class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def level_order(root): if not root: return [] queue = deque([root]) result = [] while queue: node = queue.popleft() result.append(node.val) if node.left: queue.append(node.left) if node.right: queue.append(node.right) return result ``` **知识点** 1. 广度优先搜索:用于层序遍历二叉树。 2. 队列:用于管理节点的顺序。 --- ### 算法设计题 6 **题目重述** 设计一个算法,实现在一个有序数组中插入一个新元素,并保持数组的有序性。要求时间复杂度尽可能低。 **详解** 为了在有序数组中插入一个新元素并保持有序性,可以使用二分查找法找到插入位置,然后再将数组元素移动一位。 **算法步骤**: 1. 使用二分查找法找到新元素的插入位置。 2. 将插入位置之后的所有元素向后移动一位。 3. 将新元素插入到找到的位置。 **代码概述** ```python def insert_sorted(nums, target): left, right = 0, len(nums) - 1 while left <= right: mid = (left + right) // 2 if nums[mid] == target: break elif nums[mid] < target: left = mid + 1 else: right = mid - 1 pos = max(mid, left) nums.insert(pos, target) return nums ``` **知识点** 1. 二分查找:用于高效查找插入位置。 2. 数组操作:插入和移动元素。 --- ### 算法设计题 7 **题目重述** 设计一个算法,计算给定字符串的最长公共子序列(LCS)。输入为两个字符串,输出为最长公共子序列的长度。 **详解** 可以使用动态规划来求解最长公共子序列(LCS)。定义一个二维数组 `dp`,其中 `dp[i][j]` 表示字符串 `str1` 的前 `i` 个字符和 `str2` 的前 `j` 个字符的最长公共子序列长度。 **算法步骤**: 1. 初始化二维数组 `dp`。 2. 遍历两个字符串,根据字符是否相等更新 `dp` 数组。 3. 最终结果保存在 `dp[m][n]` 中,其中 `m` 和 `n` 分别是两个字符串的长度。 **代码概述** ```python def longest_common_subsequence(str1, str2): m, n = len(str1), len(str2) dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): for j in range(1, n + 1): if str1[i - 1] == str2[j - 1]: dp[i][j] = dp[i - 1][j - 1] + 1 else: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) return dp[m][n] ``` **知识点** 1. 动态规划:用于求解最长公共子序列。 2. 状态转移方程:定义子问题之间的递推关系。 --- ### 算法设计题 8 **题目重述** 设计一个算法,判断一个整数是否为素数。 **详解** 判断一个整数是否为素数,可以通过试除法来实现。具体来说,对于一个整数 `n`,只需要检查 `2` 到 $\sqrt{n}$ 之间的整数是否能整除 `n` 即可。如果 `n` 在此范围内没有因数,则 `n` 是素数。 **算法步骤**: 1. 特殊情况处理:小于 2 的数都不是素数。 2. 从 2 开始,检查 `n` 是否能被 `2` 到 $\sqrt{n}$ 之间的任何整数整除。 3. 如果都不能整除,则 `n` 是素数。 **代码概述** ```python import math def is_prime(n): if n < 2: return False for i in range(2, int(math.sqrt(n)) + 1): if n % i == 0: return False return True ``` **知识点** 1. 试除法:用于判断素数。 2. 平方根优化:减少不必要的除法运算。 --- ### 算法设计题 9 **题目重述** 设计一个算法,计算给定字符串的所有排列组合。 **详解** 可以使用回溯法来生成字符串的所有排列组合。回溯法通过递归地交换字符位置,并逐步生成所有可能的排列组合。 **算法步骤**: 1. 定义一个递归函数,接收当前排列和剩余字符。 2. 如果剩余字符为空,则当前排列是一个完整的排列,将其加入结果列表。 3. 否则,依次选择一个字符,将其添加到当前排列中,并递归处理剩余字符。 **代码概述** ```python def permute(s): def backtrack(path, remaining, result): if not remaining: result.append(''.join(path)) return for i in range(len(remaining)): backtrack(path + [remaining[i]], remaining[:i] + remaining[i+1:], result) result = [] backtrack([], s, result) return result ``` **知识点** 1. 回溯法:用于生成排列组合。 2. 字符串操作:交换和拼接字符。 --- 希望以上解析和代码对您有所帮助!如果还有其他问题或需要进一步解释,请随时告知。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值