單鏈表的操作:包括創建(頭插法、尾插法)、刪除結點、添加結點、單鏈錶轉置、尋找兩個鏈表的共同結點、單鏈表創建環、判斷單鏈表是否有環、解環(待添加)
#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;
}
}
}