淺談圖論之拓撲排序

前言:

說起拓撲排序,第一想法就是判斷一個圖中有沒有環,然後可以作爲kruskal算法(最小生成樹)的一部分。

其次就是它的思想:

1.統計所有頂點的入度。

2.把入度爲0的頂點輸出,同時所有以此頂點爲始點的邊被抹去,即此邊的終點結點入度-1(隊列存儲)

3.如果把所有的頂點都輸出了,說明此圖無環,否則有環。

下面看一下它具體的實現過程:

嗯嗯,看了上面的是不是對拓撲排序有了一定的瞭解?

接下來,看應用:

首先對於圖的結點,如果是integer,congratulation!直接就可以用inDegree[]統計入度了

但如果是String呢?   答案是map轉換 HashMap<String,Integer>

其次,對於入度爲0的頂點的輸出有要求嗎?

沒有 Queue

有 PriorityQueue(優先隊列)

先看一段代碼普通Queue

static void topologicalSort(HashMap<String,Integer> map,ArrayList<Integer>edge[],int inDegree[],ArrayList<Integer> list,int count)
	{
		Queue<Integer>q=new LinkedList<Integer>();
		Iterator<String> iterator=map.keySet().iterator();
		while(iterator.hasNext())
		{
			String key=iterator.next();
			if(inDegree[map.get(key)]==0)//找到入度爲0的點壓入隊列
				q.add(map.get(key));
		}
		while(!q.isEmpty())
		{
			int now=q.poll();
			list.add(now);//輸出入度爲0的點
			for(int i=0;i<edge[now].size();i++)
        //把由此節點發出的所有邊消除,即邊的終點入度減一
			{
				inDegree[edge[now].get(i)]--;
				if(inDegree[edge[now].get(i)]==0)//如果上次操作產生了入度爲0的頂點,壓入隊列
					q.add(edge[now].get(i));
			}
		}
		if(list.size()!=count)
		{
			System.out.println("Error:存在環!");
			return;
		}
		System.out.println();
		for(int i=0;i<list.size();i++)
		{
			if(i==0)
				System.out.print(intToString(list.get(i),map));
			else
				System.out.print(" "+intToString(list.get(i),map));
		}
		//System.out.println();
	}

其次優先隊列(這裏只給出其之用法,具體拓撲排序的實現,可以參考上面的代碼,基本一致,這裏不再詳寫):

//這裏只寫一下優先隊列的用法,即通過類繼承Comparable接口,覆蓋CompareTo方法使類可自比
//PriorityQueue<class> pq  就實現了PriorityQueue中元素的排序。默認從小到大 
class Person implements Comparable<Person>{
    public int age;   
    Person(int age){
    this.age=age;
    }
    public int compareTo(Person other){
        
        return other.age-age;//從大到小
    
    }
    
}
public class Main {
 
    public static void main (String[] args) {
        PriorityQueue<Person> q=new PriorityQueue<Person>();
        Person a=new Person(10);
         Person b=new Person(20);
        q.offer(a);
        q.offer(b);
        System.out.println(q.peek().age);
    }
 
}

最後呢,貼出一個經典的藍橋杯拓撲排序題:

問題描述
錦瑟年華誰與度 莫問情歸處 隻影向斜陽 劍吼西風 欲把春留駐
天涯芳草無歸路 回首花無數 解語自銷魂 弱袂縈春 塵緣不相誤
......
在卡勒沃夫充滿文學殺傷力的聲音中,身處紫荊2號樓202B的四位遠近高低各不同的室友紛紛回憶起了各自波瀾起伏的過去,並對長在百草園,鄰有百花谷的現狀表達了各自的見解。
某Q:"...我小學就開竅了...她的父母說我很好,但是...今天又和北林的聯繫了..."
某X:"...差點就成了,結果到學校了...這個方法放假了我去對我的同桌用!..."
某W:"..."(千言萬語不言中,有大量的故事等待考古)
某Z:"...爲了來清華...咱們審美觀不一樣,不會搶..."
......
卡勒沃夫在這個不朽的夜話中搜集出了某人零散的歷任女友資料,爲了強迫某人將他出的題目的標程交出,現在卡勒沃夫需要一個能將這些零散信息整合起來的程序。伴隨着雄壯委婉動人的音樂,身爲程序設計快男(超女)的你降臨了!卡勒沃夫正對着您做Orz狀並請求着:"神牛啊~請施捨給我一段程序把~偶米頭髮~"。。
輸入格式
第一行爲一個不超過5的整數T,表示數據的組數。之後每組數據的一行爲一個不超過100的整數n。之後n行每行有兩個用單個空格隔開的字符串(每個字符串只有英文大小寫字母,長度不超過10),爲兩位mm的名字。每行第一個mm先於第二個mm成爲某人的女友。
在這裏我們假裝詛咒某人不會同時被兩個或兩個以上mm泡,某個mm拋棄了某人後不會再吃回頭草,同時卡勒沃夫深邃的洞察力使得他收集到了充足的信息以確定某人女友的先後順序。
在小數據組中出現的人物不超過13個
輸出格式
輸出T行,每行對應一組數據,並按照mm們從先到後成爲某人女友的順序輸出她們的名字,各個名字間用一個空格隔開。
樣例輸入
2
2
RY Unknown
YSZ RY
3
tomorrow yestoday
tomorrow today
today yestoday
樣例輸出
YSZ RY Unknown
tomorrow today yestoday

代碼如下:



import java.io.*;
import java.util.*;

/*
//example:2 
2 
RY Unknown 
YSZ RY 
3 
tomorrow yestoday 
tomorrow today 
today yestoday 
*/
public class Main {

	static ArrayList<Integer> list=new ArrayList<Integer>();//存放結果
	static ArrayList<Integer>edge[]=new ArrayList[105];//存放每個頂點對應的邊
	static HashMap<String,Integer> map=new HashMap<String,Integer>();
//實現string與整數標號的轉換
	static int inDegree[]=new int[105];//入度
	
	static void init()//初始化
	{
		list.clear();
		map.clear();
		for(int i=0;i<105;i++)
			edge[i] = new ArrayList<Integer>();
		Arrays.fill(inDegree, 0);
	}
	static String intToString(int i,HashMap<String,Integer> map)
//最後將整數標號轉化爲對應的值
	{
		Iterator<String> iterator=map.keySet().iterator();
		while(iterator.hasNext())
		{
			String key=iterator.next();
			if(map.get(key)==i)
				return key;
		}
		return null;
	}
	//拓撲排序
	static void topologicalSort(HashMap<String,Integer> map,ArrayList<Integer>edge[],int inDegree[],ArrayList<Integer> list,int count)
	{
		Queue<Integer>q=new LinkedList<Integer>();
		Iterator<String> iterator=map.keySet().iterator();
		while(iterator.hasNext())
		{
			String key=iterator.next();
			if(inDegree[map.get(key)]==0)//找到入度爲0的點壓入隊列
				q.add(map.get(key));
		}
		while(!q.isEmpty())
		{
			int now=q.poll();
			list.add(now);//輸出入度爲0的點
			for(int i=0;i<edge[now].size();i++)
//把由此節點發出的所有邊消除,即邊的終點入度減一
			{
				inDegree[edge[now].get(i)]--;
				if(inDegree[edge[now].get(i)]==0)//如果上次操作產生了入度爲0的頂點,壓入隊列
					q.add(edge[now].get(i));
			}
		}
		if(list.size()!=count)
		{
			System.out.println("Error:存在環!");
			return;
		}
		System.out.println();
		for(int i=0;i<list.size();i++)
		{
			if(i==0)
				System.out.print(intToString(list.get(i),map));
			else
				System.out.print(" "+intToString(list.get(i),map));
		}
		//System.out.println();
	}
	
	public static void main(String[] args) throws IOException {
	BufferedReader bfr=new BufferedReader(new InputStreamReader(System.in));
	String str=bfr.readLine();
	String s[]=str.split(" ");
	int n=Integer.parseInt(s[0]);
	for(int i=0;i<n;i++)
	{
		str=bfr.readLine();
		s=str.split(" ");
		int m=Integer.parseInt(s[0]);
		init();
		int count=0;
		for(int j=0;j<m;j++)
		{
			str=bfr.readLine();
			s=str.split(" ");
			String first=s[0];
			String second=s[1];
			//每個字符串對應一個整數代號
			//System.out.println(first+" "+second);
			if(!map.containsKey(first))
				map.put(first, count++);
			if(!map.containsKey(second))
				map.put(second, count++);
			
			inDegree[map.get(second)]++;//second 入度加1
			edge[map.get(first)].add(map.get(second));
		}
		
		topologicalSort(map,edge,inDegree,list,count);
	}

	}

}

emmm,總的來說拓撲排序還是很簡單的,它要求的數據結構有:一個入度數組,一個頂點邊的集合,其他也沒什麼大不了的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章