數據結構—單鏈表的操作

單鏈表的操作:包括創建(頭插法、尾插法)、刪除結點、添加結點、單鏈錶轉置、尋找兩個鏈表的共同結點、單鏈表創建環、判斷單鏈表是否有環、解環(待添加)


#include <stdio.h>
#include <stdlib.h>

//線性表的單鏈表存儲結構
typedef struct Node{
	int data;
	struct Node *next;
}SNode;

//單鏈表的初始化
SNode* LinkedListInit()
{
    SNode *L;
    L = (SNode *)malloc(sizeof(SNode));   //申請結點空間 
    if (L == NULL)                       //判斷是否有足夠的內存空間 
        printf("申請內存空間失敗\n");
    L->next = NULL;                  //將next設置爲NULL,初始長度爲0的單鏈表
	return L;
}

//頭插法建立單鏈表
SNode* headCreateList()
{
	int i;
	SNode *L;
	L = (SNode *)malloc(sizeof(SNode));	//申請頭結點空間
	L->next = NULL;						//初始化空鏈表

	int t[8] = { 12, 10, 9, 8, 7, 2, 1, 4 };	//利用尾插法建立一個以數組t的元素爲結點的單鏈表
	for ( i = 0; i < 8; ++i ) {
		SNode *p;
		p = (SNode *)malloc(sizeof(SNode));	//要添加的結點

		p->data = t[i];
		p->next = L->next;
		L->next = p;
	}
	return L;
}

//尾插法建立單鏈表
SNode* rearCreateList()
{
	int i;
	SNode *L;
	L = (SNode *)malloc(sizeof(SNode));	//申請頭結點空間
	L->next = NULL;						//初始化空鏈表

	SNode *rear;
	rear = L;	//r始終指向終端結點,開始時指向頭結點 

	int t[5] = { 5, 6, 9, 10, 12 };	//利用尾插法建立一個以數組t的元素爲結點的單鏈表
	for ( i = 0; i < 5; ++i ) {
		SNode *p;
		p = (SNode *)malloc(sizeof(SNode));	//要添加的結點

		p->data = t[i];
		rear->next = p;
		rear = p;
	}
	rear->next = NULL;
	return L;
}


/**
  * @brief 在鏈表L中第i個位置之前插入新的數據元素e,L的長度加1
  *        對於單鏈表的插入和刪除操作,其時間複雜度都是O(n),但是如果先相同位置(i)插入多個元素
  *        只需要再第一次時,找到第i個指針,此時爲O(n),接下來就是簡單的通過賦值移動指針而已
  */
int insertList(SNode *&L, int i, int e)
{
	int j = 1;
	SNode *head;
	head = L;		//pre爲前驅結點

	/*for ( j = 1; j < i; ++j ) {
		head = head->next;
	}*/

	while ( head->next != NULL && j < i ) {
		head = head->next;
		++j;
	}
	if ( head->next == NULL ) 
		return 0;
	
	SNode *p;		//要插入的新結點
	p = (SNode *)malloc(sizeof(SNode));

	p->data = e;
	p->next = head->next;
	head->next = p;

	return 1;
}

/**
  * @brief 刪除L的第i個數據元素,並用e返回其值,L的長度減1
  */
int deleteList(SNode *&L, int i, int *e)
{
	int j = 1;
	SNode *head = L;		//head爲前驅結點(頭結點)

	/*for ( j = 1; j < i; ++i ) {
		head = head->next;
	}*/

	while ( head->next && j < i ) {
		head = head->next;
		++j;
	}
	
	if ( head->next == NULL) {
		return 0;
	}

	SNode *p;		//要刪除的結點
	p = (SNode *)malloc(sizeof(SNode));

	p = head->next;
	*e = p->data;
	head->next = p->next;
	free(p);
	return 1;
}

/**
  * @brief 鏈表的轉置,針對當前結點,把其前驅結點變爲其後繼結點,後繼結點變爲前驅結點
  */
void reverseList(SNode *&L)
{
	SNode *nextNode, *preNode;
	SNode *currNode = L->next;	//當前處理的結點爲鏈表中的第一個結點
	preNode = NULL;

	while ( currNode != NULL ) {
		nextNode = currNode->next;		//保留 當前結點的下一個結點
		//對當前結點做處理
		currNode->next = preNode;		//當前結點的後繼結點爲其前驅結點
		preNode = currNode;
		currNode = nextNode;
	}
	L->next = preNode;
}

/**
  * @brief 檢測兩條鏈表是否相交,是則返回第一個交點,否則返回NULL  
  *        思路:把2個鏈表各遍歷一遍,記下長度length1和length2,若2者的尾節點指針相等,則相交。  
  *        之後再把長的鏈表從abs(len1-len2)的位置開始遍歷,第一個相等的指針爲目標節點  
  */
SNode* commonNode(SNode *head_a, SNode *head_b)
{
	int  len_a = 0;		//鏈表a的長度
	int  len_b = 0;		//鏈表b的長度
	SNode *p1 = head_a;		
	SNode *p2 = head_b;		
	int len_dif = 0;		//len_dif表示兩個鏈表長度之差
	if ( head_a == NULL || head_b == NULL )
		return NULL;
	while ( p1->next ) {
		p1 = p1->next;
		len_a++;
	}
	while ( p2->next ) {
		p2 = p2->next;
		len_b++;
	}
	
	if ( p1 != p2 ) {	//這裏要切記:兩個鏈表中如果有結點數據域和指針域都相同,並且其後面的結點
						//數據域和指針域也相同,就表示兩個鏈表有交點。(這裏要考慮兩個鏈表的結點在存儲空間的位置)
						//內存中位置不同,就算指針域和數據域相同,也不能算是有交點
		return NULL;	//如果最後一個結點不相同,返回(只要有相同的結點,最後結點必相同)
	}
	
	if ( len_a < len_b ) {
		p1 = head_b;	//p1存儲鏈表長度較長的鏈表
		p2 = head_a;	//p2存儲鏈表長度較短的鏈表
	}
	else {
		p1 = head_a;
		p2 = head_b;
	}
	len_dif = abs(len_a - len_b);
	while ( len_dif ) {
		p1 = p1->next;
		len_dif--;
	}
	
	while ( p1 ) {
		if ( p1 == p2 ) {
			return p1->next;
		}
		p1 = p1->next;
		p2 = p2->next;
	}
}

/**
  * @brief 給單鏈表建環,讓尾指針,指向第num個節點,若沒有,返回false  
  */
bool buildLoopLink(SNode *head, int num)
{
	int i = 0;
	SNode *tail = head;
	SNode *cur  = head;
	if ( head == NULL || num <= 0 ) 
		return false;
	while ( tail->next ) {
		tail = tail->next;	//tail指針指向尾指針
	}
	while ( cur->next && i < num ) {
		cur = cur->next;	//cur指針指向第num個結點
		++i;
	}
	if ( cur->next == NULL )
		return false;		//如果num值超過鏈表的結點數,返回

	tail->next = cur;
	return true;
}

/**
  * @brief 檢測單鏈表是否有環,如果有環則返回1,沒有則返回0. 解法:快慢指針
  *        這道題是《C專家編程》中的題了。其實算法也有很多,比如說:我覺得進行對訪問過的結點進行標記這個想法也不錯,
  *        而且在樹遍歷等場合我們也經常使用。但是在不允許做標記的場合就無法使用了。在種種限制的條件下,就有了上面的
  *        這種算法,其實思想很簡單:就像兩個人在操場上跑步一樣,只要有個人的速度比另一個人的速度快一點,他們肯定會
  *        有相遇的時候的。不過帶環鏈表與操場又不一樣,帶環鏈表的狀態是離散的,所以選擇走得快的要比走得慢的快多少很
  *        重要。比如說這裏,如果一個指針一次走三步,一個指針一次走一步的話,很有可能它們雖然在一個環中但是永遠遇不到,
  *        這要取決於環的大小以及兩個指針初始位置相差多少了。呵呵。你能看出兩個指針的速度應該滿足什麼關係才能在有環
  *        的情況下相遇嗎?如果你知道,不妨跟我討論一下,呵呵。
  */
bool detectLoopLink(SNode *head)
{
	SNode *quickNode = head->next;	//快指針,每次走兩步
	SNode *slowNode  = head;		//慢指針,每次走一步
	if ( head == NULL || head->next == NULL )
		return 0;
	while ( quickNode != slowNode ) {
		if ( quickNode == NULL || slowNode == NULL )
			break;
		quickNode = quickNode->next->next;
		slowNode  = slowNode->next;
	}
	if ( quickNode != NULL && slowNode != NULL )
		return 1;
	return 0;
}

/**
  * @brief 4.給單鏈表解環,爲了增加節點位圖的效率,本應使用hash或則紅黑樹,這裏不造車了,直接用 set容器
  */
bool unloopLink(SNode *head)
{
	return 0;
}
/**
  * @brief 將已有的單鏈表L重置爲空表
  */
int clearList(SNode *&L)
{
	SNode *pre;
	SNode *p;
	pre = L;		//pre爲前驅結點(頭結點)

	while ( pre->next != NULL ) {
		p = pre->next;
		pre = pre->next;
		free(p);
	}
	L->next = NULL;
	return 1;
}

/**
  * @brief 打印輸出單鏈表
  */
void printList(SNode *head)
{
    head = head->next;
    while ( head )
    {
		printf("%d ", head->data);
        head = head->next;
    }
    printf("\n");
}

int main()
{
	int element = 0;
	int postion = 0;
	int choice = 0;
	int numLoop = 0;
	int retVal;
	SNode *head, *_head, *temp;
	head = (SNode *)malloc(sizeof(SNode));
	_head = (SNode *)malloc(sizeof(SNode));

    printf("1.creat\n");  
    printf("2.delete\n");  
	printf("3.insert\n");  
    printf("4.reverse\n");  
    printf("5.commonNode\n");  
    printf("6.createLoop\n");  
	printf("7.judgeLoop\n");
    printf("0.exit\n");  

	while ( 1 ) {
		printf("please input your choice:");  
        scanf("%d", &choice);  
		
		switch ( choice ) {
		case 1 :
			//head = headCreateList();	//頭插法建立單鏈表
			head = rearCreateList();	//尾插法建立單鏈表
			printList(head);
			break;
		case 2 :
			printf("input the postion of delete element: ");
			scanf("%d", &postion);
			deleteList(head, postion, &element);
			printList(head);
			break;
		case 3 :
			printf("input the postion and element of insert: ");
			scanf("%d %d", &postion, &element);
			insertList(head, postion, element);
			printList(head);
			break;
		case 4 :
			reverseList(head);
			printList(head);
			break;
		case 5 :
			head = rearCreateList();	//尾插法建立單鏈表
			printList(head);
			_head = headCreateList();	//頭插法建立單鏈表
			printList(_head);
			temp = commonNode(head, _head);
			if ( temp != NULL )
				printf("the common node is: %d\n", temp->data);
			else
				printf("no common node!\n");
			break;
		case 6 :
			printf("input the num of rear pointer direct:");
			scanf("%d", &numLoop);
			head = rearCreateList();	//尾插法建立單鏈表
			printList(head);
			retVal = buildLoopLink(head, 3);
			if ( retVal == 0 )
				printf("create loop failed!\n");
			else
				printf("create loop success!\n");
			break;
		case 7 :
			retVal = detectLoopLink(head);
			if ( retVal == 0 )
				printf("there is not loop!\n");
			else
				printf("there is loop!\n");
			break;
		case 0 : exit(0); break;
		}
	}
}




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