程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 算法學習筆記----判斷集合S中是否存在有兩個其和等於x的元素

算法學習筆記----判斷集合S中是否存在有兩個其和等於x的元素

編輯:C++入門知識

題目:請給出一個運行時間為Θ(nlgn)的算法,使之能在給定一個由n個整數構成的集合S和另一個整數x時,判斷出S中是否存在有兩個其和等於x的元素。   解題思路: 直觀的方法是直接計算集合中兩兩元素的和,然後判斷是否存在x,但時間復雜度為Θ(n^2),不符合題目的要求,也不是一個好的解決問題的方法,下面兩種方法要好一些:  第一種是《算法導論》的教師手冊上提供的思路,構建一個輔助集合S',通過查找輔助集合S'和原集合合並後的集合中是否有重復的元素來判斷,具體步驟如下:    1)對集合S進行排序    2)構建輔助集合S',S'={z:z=x-y,y∈S},也就是說S'中的元素是x減去集合S中的元素生成    3)對集合S'進行排序    4)移除集合S中重復的元素,只保留一個,也就是說使集合S中的元素唯一。對集合S'中做同樣的處理。    5)合並集合S和S'    6)當且僅當合並的集合中存在連續的位置上出現相同的的值時,集合S中存在兩個數的和為x。(基本直譯)    這個解題思路是有問題,而且如果簡單從字面意思理解的話,這個思路是錯誤的,在某些情況下是不正確的。下面一一列出這個思路存在的問題:   a. 在生成輔助集合S'之後,才開始將集合S中的重復元素去掉只保留一個,這樣S'中也會有同樣的重復元素,為什麼不在生成輔助結合S'之前做呢?如果在第1步之後做的話,S'中的元素也是唯一的了,減少重復的工作   b. 第3步完全沒有必要,因為S在第1步中已經排好序了,所以生成的S'集合也是排好序的了,只是排序的方式不同。如果集合S是升序排列,則集合S'是降序排列。所以沒有必要再對集合S'排序,只需在合並的時候稍作處理即可。   c. 第6步中的描述原文是”There exist two elements in S whose sum is exactly x if and only if the same value appears in consecutive positions in the merged output“,如果 從字面意思理解的話,就是只要合並的集合中有重復的元素就證明結合S中存在兩個數的和為x。但是如果這麼理解的話,是不對的,比如集合S={2,3,5, 6},x=4,       則S'={2, 1,-1,-2},合並後的集合為{-2, -1, 1, 2, 2, 3, 5, 6},合並後的結合中存在重復的元素{2, 2},位置連續並且和為x,但是集合S中並沒有兩個數的和        為x。所以第6步的表述是有問題,要麼是真的錯了,要麼是語音差異理解的有問題。那要怎麼才能正確地確定呢?就是在合並的集合中必須至少有兩個重復的元素,這時      才能肯定集合S中存在兩個數的和為x。可以證明一下,假設w∈S, y∈S,則集合S'中也存在w∈S,y∈S,所以合並的集合中會有兩個重復的元素w、y。如果有多對解        的話,重復的元素個數會更多。 d. 第4步中將重復的元素唯一化,如果重復的元素的和剛好是x呢?這時豈不是反而弄巧成拙了?比如集合S={2,2,5, 6},x=4,去除重復的元素,反而錯失了找到兩個數的和為x的機會,而且題目中也沒有要求兩個和為x的元素不能重復。   還有一點需要注意的是,這個思路只是用來確定集合S中是否有兩個元素的和為x,不需要確定是哪兩個元素的和為x。   所以個人認為正確的思路應該是這樣:   1)對集合S進行排序   2)檢查集合S中是否有重復的元素,如果有則判斷重復的元素乘以2(就是兩個相加)是否為x,如果是的話,就找到了,無需做後面的處理;否則移除重復的元素,使集合S           中的元素唯一。   3)構建輔助集合S',S'={z:z=x-y,y∈S},也就是說S'中的元素是x減去集合S中的元素生成   4)合並集合S和S'   5)當合並的集合中有兩個或兩個以上的重復元素時,集合S中存在兩個元素的和為x。   接下來確定上面的思路的算法復雜度。第1步的使用歸並排序來完成,時間復雜度為Θ(nlg(n)),第2、3、4、5步的時間復雜度為Θ(n),合並起來為Θ(nlg(n))。符合題目的要求。   其代碼實現如下所示:   [cpp]  #include <stdio.h>   #include <errno.h>      #ifndef INT_MAX   #define INT_MAX     ((int)(~0U>>1))   #endif      #define ARRAY_SIZE(__s) (sizeof(__s) / sizeof(__s[0]))      static void merge(int *a, int start, int mid, int end)   {       int nl = mid - start + 1;       int nr = end - mid;       int sentinel = INT_MAX;       int left[nl + 1], right[nr + 1];       int i, j, k = start;              for (i = 0; i < nl; ++i) {           left[i] = a[k++];       }       /* Set sentinel */       left[i] = sentinel;              for (j = 0; j < nr; ++j) {           right[j] = a[k++];       }       /* Set sentinel */       right[j] = sentinel;              i = j = 0;       for (k = start; k <= end; ++k) {           if (left[i] <= right[j]) {               a[k] = left[i++];           } else {               a[k] = right[j++];           }       }   }      static void merge_sort(int *a, int start, int end)   {       int mid;              if ((start >= 0) && (start < end)) {           mid = (start + end) /2 ;                      merge_sort(a, start, mid);           merge_sort(a, mid + 1, end);                      merge(a, start, mid, end);       }   }      static int check_exist_x(int *a, int len, int x)   {       int i, j, k;       int last;       /* Just for test, should avoid */       int tmp[len];       int collection[2 * len];       int repeats;              if (len < 1) {           fprintf(stderr, "Too few elements.\n");           return -EINVAL;       }              merge_sort(a, 0, len - 1);              last = 0;       /* Remove repeat elements */       for (i = 1; i < len; ++i) {           if (a[last] == a[i]) {               if ((a[last] << 1) == x) {                   /* Found */                   return 0;               }               continue;           }           a[++last] = a[i];       }       ++last;              /* Form tmp set */       for (i = 0; i < last; ++i) {           tmp[i] = x - a[i];       }              i = 0;       j = last - 1;       k = 0;       /* Merge */       while ((i < last) && (j >= 0)) {           if (a[i] < tmp[j]) {               collection[k++] = a[i++];           } else {               collection[k++] = tmp[j--];           }       }       while (i < last) {           collection[k++] = a[i++];       }       while (j >= 0) {           collection[k++] = tmp[j--];       }              repeats = 0;       /* Check the number of repeat elements */       for (i = 1, j = 0; i < k; ++i, ++j) {           if (collection[i] == collection[j]) {               ++repeats;           }                      if (repeats >= 2) {               return 0;           }       }              return -1;   }      int main(void)   {       int source[] = { 7, 5, 2, 4, 6, 1, 5, 3};       int ret;       int x = 13;              ret = check_exist_x(source, ARRAY_SIZE(source), x);              printf("If there are two elements whose sum equals to x? %s.\n",           ret < 0 ? "No" : "Yes");       return 0;   }     第二種是使用排序+二分查找,具體步驟如下:     1)對集合S進行排序     2)從集合S中選擇一個元素S(i),計算x與S(i)的差值y=x-S(i)。在集合S中查找除S(i)之外的元素中是否存在y,如果存在,則返回。     3)檢查是否全部元素已遍歷,如果沒有跳到第2步。  接下來確定該思路的復雜度。第1步使用歸並排序來排序,時間復雜度為Θ(nlg(n));二分查找的時間復雜度為Θ(lg(n)),第2、3步需要遍歷的次數為n,因此第2、3步的時間復雜度為Θ(nlg(n)),因此總的時間復雜度為Θ(nlg(n)),符合題目的要求。     代碼實現如下所示:   [cpp]   #include <stdio.h>   #include <errno.h>      #ifndef INT_MAX   #define INT_MAX     ((int)(~0U>>1))   #endif      #define ARRAY_SIZE(__s) (sizeof(__s) / sizeof(__s[0]))      static void merge(int *a, int start, int mid, int end)   {       int nl = mid - start + 1;       int nr = end - mid;       int sentinel = INT_MAX;       int left[nl + 1], right[nr + 1];       int i, j, k = start;              for (i = 0; i < nl; ++i) {           left[i] = a[k++];       }       /* Set sentinel */       left[i] = sentinel;              for (j = 0; j < nr; ++j) {           right[j] = a[k++];       }       /* Set sentinel */       right[j] = sentinel;              i = j = 0;       for (k = start; k <= end; ++k) {           if (left[i] <= right[j]) {               a[k] = left[i++];           } else {               a[k] = right[j++];           }       }   }      static void merge_sort(int *a, int start, int end)   {       int mid;              if ((start >= 0) && (start < end)) {           mid = (start + end) /2 ;                      merge_sort(a, start, mid);           merge_sort(a, mid + 1, end);                      merge(a, start, mid, end);       }   }      static int binary_search(int *a, int len, int expect, int target)   {       int left = 0, right = len - 1, mid;              do {                  if (left == expect) {               ++left;           }                      if (right == expect) {               --right;           }                      mid = (left + right) / 2;                      if ((mid != expect) && (a[mid] == target)) {               return 0;           } else if (a[mid] > target) {               right = --mid;           } else {               left = ++mid;           }       } while (left <= right);              return -1;   }      static int check_exist_x(int *a, int len, int x)   {       int i;              if (!a || len < 2) {           fprintf(stderr, "Too few elements.\n");           return -1;       }              merge_sort(a, 0, len - 1);              for (i = 0; i < len; ++i) {           if (!binary_search(a, len, i, x - a[i])) {               return 0;           }       }              return -1;   }      int main(void)   {       int source[] = { 7, 5, 2, 4, 6, 1, 5, 3};       int x = 13;  www.2cto.com     int ret;          ret = check_exist_x(source, ARRAY_SIZE(source), x);       printf("If there are two elements whose sum equals to x? %s.\n",           ret < 0 ? "No" : "Yes");          return 0;   }  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved