哈希(3) - 推断一个数组是否为还有一个数组的子集

给定两个数组:arr1[0..m-1]和arr2[0..n-1]. 推断arr2[]是否为arr1[]的子集。

这两个数组都是无序的。

比如:

输入: arr1[] = {11, 1, 13, 21, 3, 7}, arr2[] = {11, 3, 7, 1}

输出: arr2是arr1的子集。

输入: arr1[] = {1, 2, 3, 4, 5, 6}, arr2[] = {1, 2, 4}

输出: arr2是arr1的子集。

输入: arr1[] = {10, 5, 2, 23, 19}, arr2[] = {19, 5, 3}

输出: arr2不是arr1的子集,由于arr2中的元素3,不存在于arr1中。

方法1(简单方法):

使用两个循环来处理:外层的循环遍历arr2的每一个元素。

内层的循环逐个的取元素与外层传入的元素进行比較。假设全部元素都匹配成功。则返回1,否则返回0。

#include<iostream>

//假设arr2是arr1的子集,则返回1.
bool isSubset(int arr1[], int arr2[], int numArr1, int numArr2)
{
  int i = 0;
  int j = 0;
  for (i = 0; i < numArr2; i++)
  {
    for (j = 0; j < numArr1; j++)
    {
      if (arr2[i] == arr1[j])
        break;
    }

    //假设上面的内层循环没有break, 则说明arr2不是arr1的子集
    if (j == numArr1)
      return 0;
  }

  //假设运行到这里,说明arr2是arr1的子集
  return 1;
}

int main()
{
  int arr1[] = { 11, 1, 13, 21, 3, 7 };
  int arr2[] = { 11, 3, 7, 1 };

  int numArr1 = sizeof(arr1) / sizeof(arr1[0]);
  int numArr2 = sizeof(arr2) / sizeof(arr2[0]);

  if (isSubset(arr1, arr2, numArr1, numArr2))
    std::cout<<"arr2[] is subset of arr1[]";
  else
    std::cout<<"arr2[] is not a subset of arr1[]";

  return 0;
}

时间复杂度:
O(m*n)

方法2 (使用排序和二分搜索)

1) 对arr1[]进行排序, 平均O(mLogm)

2) 对arr2[]中的每一个元素, 在已排序的arr1[]中进行二分查找.

a) 假设没有找到这个元素,则返回0.

3) 假设全部元素都找到。则返回1.

#include <iostream>

//函数声明 。

两个辅助函数,用于推断子集。

void quickSort(int *arr, int si, int ei);
int binarySearch(int arr[], int low, int high, int x);

// 假设arr2[]是arr1[]的一个子集,则返回1
bool isSubset(int arr1[], int arr2[], int numArr1, int numArr2)
{
    int i = 0;

    quickSort(arr1, 0, numArr1-1);
    for (i=0; i<numArr2; i++)
    {
        if (binarySearch(arr1, 0, numArr1-1, arr2[i]) == -1)
           return 0;
    }

    //假设运行到了这里。说明arr2是arr1的子集
    return 1;
}

//---------辅助函数begin--------

//标准的二分查找函数
int binarySearch(int arr[], int low, int high, int x)
{
  if(high >= low)
  {
    int mid = (low + high)/2;  //或者low + (high - low)/2;

    /*
     * 检測arr[mid]是否为第一次遇到x。
     * 当x满足以下情况的一种时,则证明是第一次遇到x:
     (1) (mid == 0) && (arr[mid] == x)
     (2) (arr[mid-1] < x) && (arr[mid] == x)
     */
    if(( mid == 0 || arr[mid-1] < x) && (arr[mid] == x))
      return mid;
    else if(x > arr[mid])
      return binarySearch(arr, (mid + 1), high, x);
    else
      return binarySearch(arr, low, (mid -1), x);
  }
 return -1;
}

template<typename type>
void exchange(type *a, type *b)
{
    type temp;
    temp = *a;
    *a   = *b;
    *b   = temp;
}

int partition(int A[], int si, int ei)
{
    int x = A[ei];
    int i = (si - 1);
    int j;

    for (j = si; j <= ei - 1; j++)
    {
        if(A[j] <= x)
        {
            i++;
            exchange(&A[i], &A[j]);
        }
    }
    exchange (&A[i + 1], &A[ei]);
    return (i + 1);
}

/*
实现高速排序的函数
A[]:须要排序的数组
si:Starting index
ei:Ending index
*/
void quickSort(int A[], int si, int ei)
{
    int pi;    //Partitioning index
    if(si < ei)
    {
        pi = partition(A, si, ei);
        quickSort(A, si, pi - 1);
        quickSort(A, pi + 1, ei);
    }
}
//---------辅助函数end--------

int main()
{
    int arr1[] = {11, 1, 13, 21, 3, 7};
    int arr2[] = {11, 3, 7, 1};

    int numArr1 = sizeof(arr1)/sizeof(arr1[0]);
    int numArr2 = sizeof(arr2)/sizeof(arr2[0]);

    if(isSubset(arr1, arr2, numArr1, numArr2))
      std::cout<<"arr2[] is subset of arr1[]";
    else
      std::cout<<"arr2[] is not a subset of arr1[]";

    return 0;
}

时间复杂度: O(mLogm + nLogm). 当中,mLogm是排序算法的平均复杂度。由于上面用的是高速排序,假设是最坏情况,则复杂度会变为O(m^2)。

方法3 (使用排序和归并 )

1) 对两个数组arr1和arr2分别排序。

O(mLogm + nLogn)

2) 使用归并流程来检測已排好序的数组arr2是否存在于排好序的arr1中。

//假设arr2是arr1的子集。则返回1
bool isSubset(int arr1[], int arr2[], int m, int n)
{
    int i = 0, j = 0;

    if(m < n)
       return 0;

    quickSort(arr1, 0, m-1);
    quickSort(arr2, 0, n-1);
    while( i < n && j < m )
    {
        if( arr1[j] <arr2[i] )
            j++;
        else if( arr1[j] == arr2[i] )
        {
            j++;
            i++;
        }
        else if( arr1[j] > arr2[i] )
            return 0;
    }

    if( i < n )
        return 0;
    else
        return 1;
}

时间复杂度: O(mLogm + nLogn) 。例如法2要好。

方法4 (使用哈希)

1) 给数组arr1的全部元素创建一个哈希表.

2) 遍历数组arr2。并检測当中的每一个元素是否存在于哈希表中。假设哈希表中没有找到元素,则返回0.

3) 假设全部元素都找到了。则返回1.

注意:方法1,2,4都没有处理arr2数组中有反复元素的情况。比如,{1, 4, 4, 2} 实际上不是 {1, 4, 2}的子集, 但上述的这些方法会觉得是子集。

时间: 2024-10-06 16:02:26

哈希(3) - 推断一个数组是否为还有一个数组的子集的相关文章

array_rand() 从数组中随机取出一个或多个单元

array_rand() - 从数组中随机取出一个或多个单元 mixed array_rand ( array $input [, int $num_req = 1 ] ) 从数组中取出一个或多个随机的单元,并返回随机条目的一个或多个键. 参数 input: 输入的数组.必需.规定数组. num_req: 指明了你想取出多少个单元.可选.规定返回多少随机键名.如果指定的数目超过了数组里的数量将会产生一个 E_WARNING 级别的错误. 例子 1 从数组返回一个随机键: <?php $a=arr

C++数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。(牛客剑指offer)

///////////////////////////////////////////////////////// //数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字. //例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}. //由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2. #include <iostream> using namespace std; int Grial(int a[],int n) { if(n==0)return -1;

软件工程结对开发之求一个或者多个数组中连续最大子数组之和3

一.题目要求 题目:返回一个整数数组中最大子数组的和. 要求: 输入一个整形数组,数组里有正数也有负数. 数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和. 如果数组A[0]……A[j-1]首尾相邻,允许A[i-1], …… A[n-1], A[0]……A[j-1]之和最大. 同时返回最大子数组的位置. 求所有子数组的和的最大值.要求时间复杂度为O(n). 二.设计思想 三.代码 四.截图 五.总结 六.工作合影

返回一个整数数组(环形数组)中最大子数组的和

一.实验题目 题目:返回一个整数数组中最大子数组的和. 要求:      输入一个整形数组,数组里有整数也有负数.      数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.      如果数组A[0]……A[j-1]首尾相邻,允许A[i-1],……A[n-1],A[0]……A[j-1]之和最大.      同时返回最大子数组的位置. 求所有子数组的和的最大值.要求时间复杂度为O(n). 二.实验思路 由于这次所要求的子数组相当于一个环形的数组,所以就想到了类似数字右循环的形式来

java基础:java环境,第一个Java程序,java的数组

java环境: 1. 什么是字节码和虚拟机? 2. 环境变量的设置 3.一些常用的java命令 4. 计算机如何运行java程序? 5. java的垃圾回收器 6. java的基本数据类型及转换 举例:第一个Java程序 Java的数组: 1. 一维数组 数组的声明 数组分配空间及初始化 数组的长度 两个数组之间的拷贝:System.arraycopy(array1,start,array2,start,length) 2. 二位数组 二维数组的声明及初始化

定义一个由整数组成的数组,然后输入一个整数X,如果X不在此数组中,返回小于X的最大数的位置i和大于X的最小数的位置j

//定义一个由整数组成的数组,然后输入一个整数x,如果X不在此数组中,返回小于X的最大数的位置i和大于X的最小数的位置j: //若X在此数组中,则返回数组中这个数的位置. 源代码: #include<iostream> using namespace std; void main() { int array[]={1,2,3,4,5,6,7,89,45,32,56,78,12,43,90,19};//16个数字 int x; int max=array[0]; int min=array[0]

输入6个人的成绩放入到一个一维数组中,然后打印出平均分,最后按成绩 从大到小打印。三个功能(输入是一个函数,求平均分是一个函数,排序是一个 函数)都用函数实现,最后在main方法中调用。

/*5.输入6个人的成绩放入到一个一维数组中,然后打印出平均分,最后按成绩从大到小打印.三个功能(输入是一个函数,求平均分是一个函数,排序是一个函数)都用函数实现,最后在main方法中调用.*/ #include <stdio.h> int inputScore(){ int score; scanf("%d",&score); return score;} double avg(int scores[],int length){ int i,score = 0;

php array_rand()函数从数组中随机选择一个或多个元素

php使用array_rand()函数从数组中随机选择一个或多个元素的方法. 使用array_rand() 函数从数组中随机选出一个或多个元素,并返回. array_rand(array,number) 参数 描述 array 必需.规定输入的数组参数. www.jbxue.com number 可选.默认是 1.规定返回多少个随机的元素. 例子: <?php $a=array("a"=>"Dog","b"=>"Cat

javascript如何遍历数组中的每一个元素

javascript如何遍历数组中的每一个元素:遍历数组中的所有元素是一个非常基础简单的操作,可能初学者还不够了解,下面就通过代码实例介绍一下如何实现此功能.代码如下: var theArray=["蚂蚁部落","青岛市南区","新锐科技",3]; for(var index=0;index<theArray.length;index++) { console.log(theArray[index]); } 以上代码可以遍历数组中的每一个元

在一组降序排列的数组中插入一个数据,插入后,数组中数据依然按降序排列

分析: 1.数组固定,是一个倒序的数组 2.插入一个数据,这个数据插在哪呢,要比较,与数组中所有的元素比较,这里需要一个循环,因为是降序的数组,所以当第一次遇到比自己小的,那么这个位置就是要插入的位置 3.因为上一步被占了位置,那么从这个插入的数据开始,后面的原本的数据都得向右移一位 /** * */ package com.cn.u4; import java.util.Scanner; /** * @author Administrator *向有序数组中插入学员成绩 * 在一组降序排列的数