数据结构和算法

DSA 主页 DSA 概述 DSA 环境设置 DSA 算法基础 DSA 渐近分析

数据结构

DSA 数据结构基础 DSA 数据结构和类型 DSA 数组数据结构

链接列表

DSA 链接列表数据结构 DSA 双向链接列表数据结构 DSA 循环链表数据结构

堆栈 &队列

DSA 堆栈数据结构 DSA 表达式解析 DSA 队列数据结构

搜索算法

DSA 搜索算法 DSA 线性搜索算法 DSA 二分搜索算法 DSA 插值搜索 DSA 跳跃搜索算法 DSA 指数搜索 DSA 斐波那契搜索 DSA 子列表搜索 DSA 哈希表

排序算法

DSA 排序算法 DSA 冒泡排序算法 DSA 插入排序算法 DSA 选择排序算法 DSA 归并排序算法 DSA 希尔排序算法 DSA 堆排序 DSA 桶排序算法 DSA 计数排序算法 DSA 基数排序算法 DSA 快速排序算法

图形数据结构

DSA 图形数据结构 DSA 深度优先遍历 DSA 广度优先遍历 DSA 生成树

树数据结构

DSA 树数据结构 DSA 树遍历 DSA 二叉搜索树 DSA AVL 树 DSA 红黑树 DSA B树 DSA B+ 树 DSA 伸展树 DSA 尝试 DSA 堆数据结构

递归

DSA 递归算法 DSA 使用递归的汉诺塔 DSA 使用递归的斐波那契数列

分而治之

DSA 分而治之 DSA 最大最小问题 DSA 施特拉森矩阵乘法 DSA Karatsuba 算法

贪婪算法

DSA 贪婪算法 DSA 旅行商问题(贪婪方法) DSA Prim 最小生成树 DSA Kruskal 最小生成树 DSA Dijkstra 最短路径算法 DSA 地图着色算法 DSA 分数背包问题 DSA 作业排序截止日期 DSA 最佳合并模式算法

动态规划

DSA 动态规划 DSA 矩阵链乘法 DSA Floyd Warshall 算法 DSA 0-1 背包问题 DSA 最长公共子序列算法 DSA 旅行商问题(动态方法)

近似算法

DSA 近似算法 DSA 顶点覆盖算法 DSA 集合覆盖问题 DSA 旅行商问题(近似方法)

随机算法

DSA 随机算法 DSA 随机快速排序算法 DSA Karger 最小割算法 DSA Fisher-Yates 洗牌算法

DSA 有用资源

DSA 问答 DSA 快速指南


子集和问题

子集和问题

子集和问题中,给定一个包含非负整数元素的集合,并给出另一个和值,我们的任务是找出该集合中所有和值与给定和值相同的可能子集。

集合:用数学术语来说,集合被定义为相似类型对象的集合。集合中的实体或对象必须通过相同的规则相互关联。

子集:假设有两个集合,即集合 P 和集合 Q。集合 P 被称为集合 Q 的子集,仅当集合 P 的所有元素也属于集合 Q,反之亦然,这不一定成立。

输入输出场景

假设给定集合,其和为 -

 Set = {1, 9, 7, 5, 18, 12, 20, 15}
 sum value = 35

给定集合的所有可能子集,其中每个子集的每个元素的和与给定的和值相同,如下所示 −

 {1  9  7  18}  
 {1  9  5  20}  
 {5  18  12}

回溯法求解子集和问题

在朴素方法求解子集和问题时,算法会生成所有可能的排列,然后逐一检查是否存在有效解。只要某个解满足约束条件,就将其标记为解的一部分。

在求解子集和问题时,回溯法用于选择有效子集。当某个元素无效时,我们将回溯以获取前一个子集,并添加另一个元素以获得解。

在最坏情况下,回溯法可能会生成所有组合,但通常情况下,它的性能优于朴素方法。

按照以下步骤使用回溯法求解子集和问题 −

  • 首先,取一个空子集。

  • 将下一个元素(索引 0 处的元素)添加到空集中。

  • 如果子集等于和值,则将其标记为解的一部分。

  • 如果子集不是解,且小于和值,则将下一个元素添加到子集中,直到找到有效解。

  • 现在,移动到集合中的下一个元素并检查另一个解,直到尝试完所有组合。

示例

在此示例中,我们将说明如何在各种编程语言中解决子集和问题。

#include <stdio.h>
#define SIZE 7
void displaySubset(int subSet[], int size) {
   for(int i = 0; i < size; i++) {
      printf("%d  ", subSet[i]);
   }
   printf("
");
}
void subsetSum(int set[], int subSet[], int n, int subSize, int total, int nodeCount ,int sum) {
   if( total == sum) {
      //打印子集
      displaySubset(subSet, subSize);
      //其他子集
      if (subSize != 0)
         subsetSum(set,subSet,n,subSize-2,total-set[nodeCount],nodeCount+1,sum);     
      return;
   }else {
      //沿宽度查找节点
      for( int i = nodeCount; i < n; i++ ) {     
         subSet[subSize] = set[i];
          //对下一个节点进行深度操作
         subsetSum(set,subSet,n,subSize+1,total+set[i],i+1,sum);    
      }
   }
}
void findSubset(int set[], int size, int sum) {
   //创建子集数组来传递subsetSum的参数
   int subSet[size];     
   subsetSum(set, subSet, size, 0, 0, 0, sum);
}
int main() {
   int weights[] = {1, 9, 7, 5, 18, 12, 20, 15};
   int size = SIZE;
   findSubset(weights, size, 35);
   return 0;
}
#include <iostream>
using namespace std;
void displaySubset(int subSet[], int size) {
   for(int i = 0; i < size; i++) {
      cout << subSet[i] << "  ";
   }
   cout << endl;
}
void subsetSum(int set[], int subSet[], int n, int subSize, int total, int nodeCount, int sum) {
   if( total == sum) {
      //打印子集
      displaySubset(subSet, subSize);  
      //对于其他子集
      subsetSum(set, subSet, n, subSize-1, total-set[nodeCount], nodeCount+1,sum);     
      return;
   }else {
      //沿宽度查找节点
      for( int i = nodeCount; i < n; i++ ) {     
         subSet[subSize] = set[i];
          //do for next node in depth
         subsetSum(set, subSet, n, subSize+1, total+set[i], i+1, sum);    
      }
   }
}
void findSubset(int set[], int size, int sum) {
   //创建子集数组来传递subsetSum的参数
   int *subSet = new int[size];     
   subsetSum(set, subSet, size, 0, 0, 0, sum);
   delete[] subSet;
}
int main() {
   int weights[] = {1, 9, 7, 5, 18, 12, 20, 15};
   int size = 7;
   findSubset(weights, size, 35);
}
public class Main {
   static void displaySubset(int subSet[], int size) {
      for(int i = 0; i < size; i++) {
         System.out.print(subSet[i] + "  ");
      }
      System.out.println();
   }
   static void subsetSum(int set[], int subSet[], int n, int subSize, int total, int nodeCount ,int sum) {
      if( total == sum) {
         //打印子集
         displaySubset(subSet, subSize);
         //其他子集
         if (subSize != 0)
            subsetSum(set,subSet,n,subSize-1,total-set[nodeCount],nodeCount+1,sum);     
         return;
      } else {
         //沿宽度查找节点
         for( int i = nodeCount; i < n; i++ ) {     
            subSet[subSize] = set[i];
            //对下一个节点进行深度操作
            subsetSum(set,subSet,n,subSize+1,total+set[i],i+1,sum);    
         }
      }
   }
   static void findSubset(int set[], int size, int sum) {
      //创建子集数组来传递 subsetSum 的参数
      int subSet[] = new int[size];     
      subsetSum(set, subSet, size, 0, 0, 0, sum);
   }
   public static void main(String[] args) {
      int weights[] = {1, 9, 7, 5, 18, 12, 20, 15};
      int size = 7;
      findSubset(weights, size, 35);
   }
}
def displaySubset(subSet, size):
    for i in range(size):
        print(subSet[i], end="  ")
    print()

def subsetSum(set, subSet, n, subSize, total, nodeCount, sum):
    if total == sum:
        #打印子集
        displaySubset(subSet, subSize)
        #对于其他子集
        if subSize != 0:
            subsetSum(set, subSet, n, subSize-1, total-set[nodeCount], nodeCount+1, sum)
        return
    else:
        #沿宽度查找节点
        for i in range(nodeCount, n):
            subSet[subSize] = set[i]
            #do for next node in depth
            subsetSum(set, subSet, n, subSize+1, total+set[i], i+1, sum)

def findSubset(set, size, sum):
    #创建子集数组来传递 subsetSum 的参数
    subSet = [0]*size
    subsetSum(set, subSet, size, 0, 0, 0, sum)

if __name__ == "__main__":
    weights = [1, 9, 7, 5, 18, 12, 20, 15]
    size = 7
    findSubset(weights, size, 35)

输出

1  9  7  18  
1  9  5  20  
5  18  12