天天看點

找水王02

    第一次練習的題目:

    三人行設計了一個灌水論壇。資訊學院的學生都喜歡在上面交流灌水,傳說在論壇上有一個“水王”,他不但喜歡發帖,還會回複其他ID發的每個文章。坊間風聞該“水王”發帖數目超過了文章數目的一半。如果你有一張目前論壇的文章(包括回帖)清單,其中文章的作者的ID也在其中,你能快速的找到這個傳說中的水王嗎?

    第二次練習的題目是基于第一次的練習題目:

    随着論壇的發展,管理者發現水王沒有了,但是統計結果表明,有三個發帖很多的ID。據統計他們的發帖數量超過了1/4,你能從發帖清單中快速找到他們嗎?

    程式設計思想:

    第二個程式練習的思想同樣是基于第一次的基礎,簡單而言,第一個程式要解決的問題無非就是将一個清單中的ID号通過一次循環分成兩類,第一類就是“水王”的ID号,在清單中占據一半以上,第二類就是其他使用者的ID,通過兩兩比較删掉不同的ID号以減少總數量的方式來找到“水王”,因為通過删減,無論“水王”的ID是否在這兩個删掉的ID号中,最終剩下的清單中的“水王”的ID仍然占據一半以上。這裡所講的删減并沒有真正的去改變ID清單中的内容,而是利用一個變量來記錄“水王”出現的次數,通過增加和減少這個變量的值來虛拟的實作ID号的删減。

    第二次的程式設計同樣基于這樣的思想,不過與第一次相比最大的不同在于,要在清單中查找到三個不同的ID号,并假設這三個ID号就是“小水王”,當某一個ID号與這三個ID号都不想同時才能對這四個ID号進行同時的删減。

    程式的概要設計如下:

    我的程式中首先假設清單中有十個使用者ID,使用者的ID号分别用0-9十個數字字元來代替,利用随機函數,生成N條記錄,即将使用者的ID号儲存在一個長度為N的一維數組中。程式的起始同樣用随機數生成了三個自定義的“小水王”,利用while循環保證了這三個ID号是不同的,在後續的程式中通過不重複随機替換的方式,確定了一維數組中的自定義的三個“小水王”ID号都在四分之一以上。查找小水王的核心程式從清單的第一個ID号開始查找,查找到三個不同的ID号後,假設初始的這三個ID号分别為三個“小水王”,對于其後的每一個ID進行循環比較,根據記錄“小水王”出現次數的變量的值是否為零可以得到八種情況(即:000、001、010、011、100、101、110、111),這八種情況對應了八個不同的分支以及八種不同的操作。當變量的值都不為零時,即第八種情況,利用找水王01的思想,當遇到一個ID與這三個ID号都不同時,則将其都删掉。程式的詳細設計見程式源代碼中的注釋。

    程式源代碼:

//現有一張灌水論壇的文章清單,有三個“小水王”發帖的數目都超過了文章數目的四分之一,找出這三個“小水王”的ID
//05-27,2016,Hailin Song

#include<iostream>
#include<stdlib.h>
#include<time.h>
using namespace std;

#define N 100

int main()
{
	srand((int)time(NULL));                 //定義時間種子

	int XiaoShuiWang1, XiaoShuiWang2, XiaoShuiWang3;

	XiaoShuiWang1 = 0 + rand() % 10;        //假設文章清單中有10個ID号,分别用0-9這十個數字字元代替,自定義第一個“小水王”的ID
	XiaoShuiWang2 = 0 + rand() % 10;        //假設文章清單中有10個ID号,分别用0-9這十個數字字元代替,自定義第二個“小水王”的ID
	while (XiaoShuiWang1 == XiaoShuiWang2)  //確定第一個“小水王”和第二個“小水王”的ID号不同
	{
		XiaoShuiWang2 = 0 + rand() % 10;
	}
	XiaoShuiWang3 = 0 + rand() % 10;        //假設文章清單中有10個ID号,分别用0-9這十個數字字元代替,自定義第三個“小水王”的ID
	while ((XiaoShuiWang1 == XiaoShuiWang3) || (XiaoShuiWang2 == XiaoShuiWang3)) //確定第三個“小水王”的ID和第一個、第二個“小水王”的ID都不同
	{
		XiaoShuiWang3 = 0 + rand() % 10;
	}
	cout << "自定義的三個“小水王”的ID分别為:";
	cout << XiaoShuiWang1 << " " << XiaoShuiWang2 << " " << XiaoShuiWang3 << endl; //輸出自定義的三個“小水王”的ID,同時測試“找小水王”程式的結果是否正确
	cout << endl;
	
	int Tiezi[N];                           //假設文章清單中有N條記錄
	int numberlocation = (N / 4 + 1) * 3;   //記錄需要替換的位置
	int location[(N / 4 + 1) * 3];        
	for (int i = 0; i < N; i++)             //随機生成文章清單
	{
		Tiezi[i] = 0 + rand() % 10;
	}

	for (int i = 0; i < numberlocation; i++)     //循環執行((N / 4 + 1) * 3)次,確定文章清單中三個“小水王”的ID都超過總數N的四分之一
	{
		location[i] = 0 + rand() % N;            //确定文章清單中随機替換的位置
		for (int j = 0; j < i; j++)
		{
			if (location[i] == location[j])      //確定随機替換的位置不會發生重複
			{
				location[i] = 0 + rand() % N;
				j = -1;
			}
		}
	}

	for (int i = 0; i < numberlocation / 3; i++)
	{
		Tiezi[location[i]] = XiaoShuiWang1;        //用第一個“小水王”的ID替換随機産生的使用者的ID
	}
	for (int i = numberlocation / 3; i < numberlocation / 3 * 2; i++)
	{
		Tiezi[location[i]] = XiaoShuiWang2;        //用第二個“小水王”的ID替換随機産生的使用者的ID
	}
	for (int i = numberlocation / 3 * 2; i < numberlocation; i++)
	{
		Tiezi[location[i]] = XiaoShuiWang3;        //用第三個“小水王”的ID替換随機産生的使用者的ID
	}

	cout << "文章清單的ID如下所示:" << endl;
	for (int i = 0; i < N; i++)
	{
		cout << Tiezi[i] << " ";
		if (i % 10 == 9)
		{
			cout << endl;
		}
	}
	cout << endl;

	//找“小水王”的核心程式,其基本思想是将文章清單分為四類,即小水王一、小水王二、小水王三和其他ID
	int temp;       //記錄i的位置
	int SelectShuiWang1, SelectShuiWang2, SelectShuiWang3;     //這三個變量用于儲存程式中找出的“小水王”的ID            
	int shuiwangcount1 = 0, shuiwangcount2 = 0, shuiwangcount3 = 0;  //這三個變量用于記錄“小水王”出現的次數,也用做判斷條件                    

	SelectShuiWang1 = Tiezi[0];           //假定文章清單中的第一個元素即使第一個“小水王”
	shuiwangcount1 = 1;
	for (int i = 1; i < N; i++)           //從第二個元素開始查找,直到找到與第一個ID不同的ID号為止,把這個ID号假定為第二個“小水王”的ID
	{
		while (SelectShuiWang1 == Tiezi[i])
		{
			shuiwangcount1++;
			i = i + 1;
		}
		temp = i;
		SelectShuiWang2 = Tiezi[temp];
		shuiwangcount2 = 1;
		i = N;
	}
	for (int i = temp + 1; i < N; i++)   //從假定的第二個“小水王”的後一個ID号開始,直到找到與第一個、第二個ID号都不同的ID号為止,把這個ID号定義為第三個“小水王”的ID,進而確定程式假定了三個不同的ID
	{
		if (SelectShuiWang1 == Tiezi[i])
		{
			shuiwangcount1++;
		}
		else
		{
			if (SelectShuiWang2 == Tiezi[i])
			{
				shuiwangcount2++;
			}
			else
			{
				temp = i;
				SelectShuiWang3 = Tiezi[i];
				shuiwangcount3 = 1;
				i = N;
			}
		}
	}

	for (int i = temp + 1; i < N; i++)   //從假定的第三個“小水王”的後一個ID号開始,一直循環到結束,每一個分支分别對應一種情況,共八個大的分支
	{
		if ((0 == shuiwangcount1) && (0 == shuiwangcount2) && (0 == shuiwangcount3))  //三個記錄次數的變量都為零的情況,即表明前面假定的三個“小水王”并不是真正的“小水王”,是以,程式需要再次假定新的“小水王”
		{
			SelectShuiWang1 = Tiezi[i];
			shuiwangcount1++;
		}
		else if ((0 == shuiwangcount1) && (0 == shuiwangcount2) && (0 != shuiwangcount3)) //若第三個小水王的次數不為零,則再次假定另外的“小水王”時,需要先判斷後一個ID号是否和第三個“小水王”的ID号相同,以下分支都是這同一樣的原理
		{
			if (SelectShuiWang3 == Tiezi[i])
			{
				shuiwangcount3++;
			}
			else
			{
				SelectShuiWang1 = Tiezi[i];
				shuiwangcount1++;
			}
		}
		else if ((0 == shuiwangcount1) && (0 != shuiwangcount2) && (0 == shuiwangcount3))
		{
			if (SelectShuiWang2 == Tiezi[i])
			{
				shuiwangcount2++;
			}
			else
			{
				SelectShuiWang1 = Tiezi[i];
				shuiwangcount1++;
			}
		}
		else if ((0 == shuiwangcount1) && (0 != shuiwangcount2) && (0 != shuiwangcount3))
		{
			if (SelectShuiWang2 == Tiezi[i])
			{
				shuiwangcount2++;
			}
			else if (SelectShuiWang3 == Tiezi[i])
			{
				shuiwangcount3++;
			}
			else
			{
				SelectShuiWang1 = Tiezi[i];
				shuiwangcount1++;
			}
		}
		else if ((0 != shuiwangcount1) && (0 == shuiwangcount2) && (0 == shuiwangcount3))
		{
			if (SelectShuiWang1 == Tiezi[i])
			{
				shuiwangcount1++;
			}
			else
			{
				SelectShuiWang2 = Tiezi[i];
				shuiwangcount2++;
			}
		}
		else if ((0 != shuiwangcount1) && (0 == shuiwangcount2) && (0 != shuiwangcount3))
		{
			if (SelectShuiWang1 == Tiezi[i])
			{
				shuiwangcount1++;
			}
			else if (SelectShuiWang3 == Tiezi[i])
			{
				shuiwangcount3++;
			}
			else
			{
				SelectShuiWang2 = Tiezi[i];
				shuiwangcount2++;
			}
		}
		else if ((0 != shuiwangcount1) && (0 != shuiwangcount2) && (0 == shuiwangcount3))
		{
			if (SelectShuiWang1 == Tiezi[i])
			{
				shuiwangcount1++;
			}
			else if (SelectShuiWang2 == Tiezi[i])
			{
				shuiwangcount2++;
			}
			else
			{
				SelectShuiWang3 = Tiezi[i];
				shuiwangcount3++;
			}
		}
		else      //當記錄“小水王”次數的三個變量都不為零時,證明其出現的次數多,當遇到某一個ID号都不同于這三個ID号時,則将其數量全部減一,即虛拟性的删去這四個不同的ID,但剩餘的ID号清單中三個“小水王”的ID仍然占四分之一以上,進而利用了找水王1的程式原理
		{
			if (SelectShuiWang1 == Tiezi[i])
			{
				shuiwangcount1++;
			}
			else if (SelectShuiWang2 == Tiezi[i])
			{
				shuiwangcount2++;
			}
			else if (SelectShuiWang3 == Tiezi[i])
			{
				shuiwangcount3++;
			}
			else
			{
				shuiwangcount1--;
				shuiwangcount2--;
				shuiwangcount3--;
			}
		}
	}
	
	cout << "友情提示:隻要程式找出的ID号與自定義的ID号相同即結果正确,無先後順序!" << endl;
	cout << "程式找出的“小水王”的ID為:";
	cout << SelectShuiWang1 << " " << SelectShuiWang2 << " " << SelectShuiWang3 << endl;      //輸出程式找出的“小水王”ID,與自定義的“小水王”ID作比較,判斷程式的邏輯是否正确,“小水王”的ID沒有先後順序

	cout << endl;

	return 0;
}
      

    程式運作結果如下所示:

找水王02
找水王02

    個人總結如下:

    雖然找水王02是從找水王01的基礎上進行的,思想也是來源于找水王01,但是這次的程式卻比上次複雜的,因為三個“小水王”需要考慮的因素就要比第一個程式考慮的内容多,是以,為了寫好這個程式花費了我将近六個小時的時間。在程式編寫的過程中有兩個比較困惑的點,第一個點就是如何通過循環從清單的初始位置找到三個不同的暫定假設的“小水王”的ID,本來想通過比較優化的方法寫出,但是濕了好幾種辦法,覺得沒有什麼更優化的算法,就用最笨的循環控制寫了出來,第二個點就是如何保證三個“小水王”的删減是正确的,當小水王的記錄次數删減為零時,需要再把下一個元素假設為“小水王”,但是這時需要加以判斷,即該元素是否和當下正在假設的“小水王”的ID相同,經過多次的嘗試和多次的考慮,最終總結出了(000、001、010、011、100、101、110、111)這八種情況。經過多次的測試,程式暫時沒有出現邏輯上的錯誤。

    寫完這個程式以後,首先認識到了if、else if、else文法的重要作用,這種文法适合于多種分支的情況,而且每次循環都執行一個分支,可以将程式的邏輯處理功能清晰的分成多個不同的分支,便于了解。另一個感受就是寫這樣的程式挺鍛煉邏輯思維能力的,因為如果程式的邏輯執行不清楚的,那就不可能正确的劃分出分支,更不能很好的寫出判斷條件以及程式該執行的操作。