首頁 > 軟體

C語言單雙線性及迴圈連結串列與範例

2023-03-25 06:01:10

連結串列思維

順序儲存結構

Operation
InitList(*L):初始化操作,簡歷一個空的線性表L
ListEmpty(L):判斷線性表是否為空表,若線性表為空返回true,否則返回false
ClearList(*L):將線性表清空
GetElem(L,i,*e):將線性表L中的第i個位置返回給e
LocatElem(L,e):線上性表L中查詢與給定值e相等的元素,如果查詢成功,返回該元素在表中序號表示成功,否則,返回0表示失敗
ListInsert(*L,i,e):線上性表L中第i個位置插入元素e
ListDelete(*L,i,*e):刪除線性表L中的第i個位置的元素,並用e返回其值
ListLength(L):返回線性表L的元素個數

單連結串列

線性表的鏈式儲存結構的特點是用一組任意的儲存單元儲存線性表的資料元素,這組儲存單元可以存在記憶體中未被佔用的任意位置。
比起順序儲存結構每個資料元素只需要儲存一個位置就可以了。
現在鏈式儲存結構中,除了要儲存資料元素資訊外,還要儲存它的後繼元素的儲存地址(指標)
我們把儲存資料元素資訊的域稱為資料域,把儲存直接後繼位置的域稱為指標域。指標域中儲存的資訊稱為指標。這兩部分資訊組成資料元素稱為儲存映像,稱為結點(Node)。
n個結點連結成一個連結串列,即為線性表(a1,a2,a3, …., an)的鏈式儲存結構。
因為此連結串列的每個結點中只包含一個指標域,所以叫做單連結串列。

單連結串列儲存結構

 獲得連結串列第i個資料的演演算法思路:

  • 宣告一個結點p指向連結串列第一個結點,初始化從1開始
  • 當j<i時,就遍歷連結串列,讓p的指標向後移動,不斷指向一下結點,j+1;
  • 若到連結串列末尾p為空,則說明第i個元素不存在;
  • 否則查詢成功,返回結點p的資料。

 單連結串列的讀取

  • 通俗易懂就是從頭開始找,直到第i個元素為止。
  • 由於這個演演算法的時間複雜度取決於i的位置,當i=1時,則不需要遍歷,而i=n時則遍歷n-1次才可以。因此最壞情況的時間複雜度為O(n)。
  • 由於單連結串列的結構中沒有定義表長,所以不能實現知道要回圈多少次,因此也就不方便使用for控制迴圈。
  • 其核心思想叫做“工作指標後移”,這其實也是很多演演算法的常用技術。

單連結串列的插入

 看圖發現我們插入結點不需要向順序儲存一樣全部更改,只需要讓s -> next和p -> next發生一點指向改變:

  • s -> next = p-> next
  • p -> next = s

如圖:

單連結串列第i個資料插入結點的演演算法思路:

1.宣告一結點p指向連結串列頭結點,初始化j從1開始;-當j<i時,就遍歷連結串列,讓p的指標向後移動,不斷指向下一結點,j累加1;
2.若到連結串列末尾p為空,則說明第i個元素不存在;
3.否則查詢成功,在系統中生成一個空結點S;
4.將資料元素e賦值給s->data;
5.單連結串列的插入剛才兩個標準語句;
6.返回成功

  • (表示型別)malloc(sizeof(表示長度))開闢一個空間

 單連結串列的刪除

假設元素a2的結點為q,要實現結點q刪除單連結串列的操作,其實就是將它的前繼結點的指標繞過指向後面。我們要做的就是上一步
可以寫成 p -> next = p ->next -> next
也可以使用
q = p -> next;p -> next = q -> next

單連結串列第i個資料刪除結點的演演算法思路:

  • 宣告結點p指向連結串列第一個結點,初始化j=1;
  • 當j < i時,就遍歷連結串列,讓P的指標向後移動,不斷指向下一個結點,j累加1;
  • 一若到連結串列末尾p為空,則說明第i個元素不存在;
  • 否則查詢成功,將欲刪除結點p->next賦值給q;
  • 單連結串列的刪除標準語句p -> next = q -> next ;
  • 將q結點中的資料賦值給e,作為返回;
  • 釋放q結點。free(q)

 單連結串列的整表建立

建立單連結串列的過程是一個動態生成連結串列的過程,從“空表“的初始狀態起,依次建立各元素結點並逐個插入連結串列。

所以單連結串列整表建立的演演算法思路如下:

  • 宣告一結點p和計數器變數i;
  • 初始化一空連結串列L;
  • 讓L的頭結點的指標指向NULL,即建立一個帶頭結點的單連結串列;
  • 迴圈實現後繼結點的賦值和插入。

 頭插法建立單連結串列

頭插法從一個空表開始,生成新結點,讀取資料存放到新結點的資料域中,然後將新結點插入到當前連結串列的表頭上,直到結束為止。

簡單來說,就是把新加進的元素放在表頭後的第個位置:

  • 先讓新節點的next指向頭節點之後
  • 然後讓表頭的next指向新節點

 嗯,用現實環境模擬的話就是插隊的方法,始終讓新結點插在第一的位置。

尾插法建立單連結串列

單連結串列的整表刪除

  • 宣告結點p和q;
  • 將第一個結點賦值給p,下一結點賦值給q;
  • 迴圈執行釋放p和將q賦值給p的操作;
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
Status ClearList(LinkList *L){
	LinKList p,q;
	p = (*L) -> next;
	while(p){
		q = p -> next;
		free(p);
		p = q;
	}
	(*L) -> next = NULL;
	return OK
}
//這段演演算法程式碼裡,常見的錯誤就是有同學會覺得q變數沒有存在的必要,只需要在迴圈體內直接寫自由(P);p=p->下一步;即可?
//可這個世上沒有無緣無故的愛,這樣做會帶來什麼問題呢?
//要知道p是一個結點,它除了有資料域,還有指標域.當我們做自由(P);時候,其實是對它整個結點進行刪除和記憶體釋放的工作.而我們整表刪除是需要一個個結點刪除的,所以我們就需要q來記載p的下一個結點.

 單連結串列範例

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

struct singleList{
	int data;
	struct singleList *next;
};
//建立一個表
struct singleList*createList(){
	//指標變成結構體變數  
	struct singleList *headNode = (struct singleList *)malloc(sizeof(struct singleList));
	headNode -> next = NULL; //差異化處理,充當表頭 
	return headNode; 
}
//建立結點:  戰門建立新的結點
//int data
struct singleList* creatNode(int data) {
	struct singleList* newNode = (struct singleList *)malloc(sizeof(struct singleList));
	newNode -> data = data;
	newNode -> next = NULL;
	return newNode;
}
void insertNodeByHead(struct singleList *headNoed,int data){
	 struct singleList *newNode = creatNode(data);
	 newNode -> next = headNoed -> next;
	 headNoed -> next = newNode;
}

void printList(struct singleList* headNode){
	
	//因為第一個結點就是表頭,所以 裡面沒有資料,要從第二個列印
	struct singleList *pMove = headNode -> next;
	while (pMove) {
		printf("%d -> ",pMove -> data);
		pMove = pMove -> next;
	}
	printf("n");
}
int main(){
	struct singleList*list =createList();
	insertNodeByHead(list,1);
	insertNodeByHead(list,2);
	insertNodeByHead(list,3);
	printList(list);
	system("pause");
	return 0;
}
 
 

單連結串列學生系統簡單範例

此處使用c++語法,請自行切換

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
struct student {
	char name[20];
	int age;
	int num;
};



struct singleList {
	//	int data;
	struct student data;
	struct singleList* next;
};
//建立一個表
struct singleList* createList() {
	//指標變成結構體變數  
	struct singleList* headNode = (struct singleList*)malloc(sizeof(struct singleList));
	headNode->next = NULL; //差異化處理,充當表頭 
	return headNode;
}
//建立結點:  戰門建立新的結點
//int data
struct singleList* creatNode(struct student data) {
	struct singleList* newNode = (struct singleList*)malloc(sizeof(struct singleList));
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}
void insertNodeByHead(struct singleList* headNoed, struct student data) {
	struct singleList* newNode = creatNode(data);
	newNode->next = headNoed->next;
	headNoed->next = newNode;
}

void printList(struct singleList* headNode) {

	//因為第一個結點就是表頭,所以 裡面沒有資料,要從第二個列印
	struct singleList* pMove = headNode->next;
	printf("請錄入資訊:姓名t年齡t編號n");
	while (pMove) {
		printf("%st%dt%dn ", pMove->data.name, pMove->data.age, pMove->data.num);
		//		struct student data
		//		printf("%d -> ",pMove -> data);
		pMove = pMove->next;
	}
	printf("n");
}

//增刪查改
//void saveInfoToFile() 
//選單
void printMenu() {
	printf("---------------------n");
	printf("tto.退出資訊n");
	printf("tt1.錄入資訊n");
	printf("tt2.顯示資訊n");
	printf("---------------------n");
}
//按鍵處理 
struct singleList* list = createList();
void keyDown() {
	int choice = 0;
	scanf("%d", &choice);
	struct student tempDate;
	switch(choice)
	{
	case 0:
		printf("正常退出n");
		system("pause");
		break;
	case 1:
		printf("請輸入學生姓名年齡編號");
		scanf("%s %d %d", tempDate.name, &tempDate.age, &tempDate.num);
		insertNodeByHead(list, tempDate);
		break;
	case 2:
		printList(list);
		break;
	};
}


int main() {
	//	struct singleList*list =createList();
	//	insertNodeByHead(list,1);
	//	insertNodeByHead(list,2);
	//	insertNodeByHead(list,3);
	//	printList(list);

	while (1) {
		printMenu();
		keyDown();
		system("pause");
		system("cls");

	}
	system("pause");
	return 0;
};


//1.先寫連結串列,先寫資料結構
//2.修改data
//3.系統應用開發  

雙向連結串列

雙連結串列 

  • main.h
#pragma once
#include <stdio.h>
#include <stdlib.h>

//雙向連結串列結構體
typedef struct doubleLink {
    char data;//記錄節點所在的行和列
    struct doubleLink* pre;//指向前驅節點的指標
    struct doubleLink* next;//指向後續節點的指標
};

//初始化雙連結串列
struct doubleLink* initDoubleLink();
//查詢
struct doubleLink* selectDoubleLink(struct doubleLink* p, int site);
//插入
struct doubleLink* insertDoubleLink(struct doubleLink* p, int site, int value);
//刪除
struct doubleLink* deleteDoubleLink(struct doubleLink* p, int site);
//修改
struct doubleLink* updateDoubleLink(struct doubleLink* p, int site, int value);
//列印
void display(doubleLink* head);
  • main.cpp
#include "main.h"

int main() {
	printf("雙連結串列n");

	struct doubleLink* doubleLink = NULL;
	display(doubleLink);

	doubleLink = initDoubleLink();
	display(doubleLink);
	
	insertDoubleLink(doubleLink, 2, 100);
	display(doubleLink);

	deleteDoubleLink(doubleLink, 2);
	display(doubleLink);

	updateDoubleLink(doubleLink, 2, 100);

	display(doubleLink);

	return 0;
}

  • doubleLink.cpp
#include "main.h"

struct doubleLink* initDoubleLink() {
	doubleLink* headNode = NULL;
	doubleLink* temp = NULL;

	headNode = (doubleLink*)malloc(sizeof(doubleLink));

	headNode->data = 0;
	headNode->next = NULL;
	headNode->pre = NULL;

	temp = headNode;

	for (int i = 1; i <= 4; i++)
	{
		doubleLink* a = (doubleLink*)malloc(sizeof(doubleLink));
		a->data = i;
		a->next = NULL;
		a->pre = temp;

		temp->next = a;
		temp = temp->next;
	}
	return headNode;
}

struct doubleLink* insertDoubleLink(struct doubleLink* p, int site, int value) {
	doubleLink* body = NULL;
	body = p;
	doubleLink* temp = NULL;

	temp = (doubleLink*)malloc(sizeof(doubleLink));
	temp->data = value;
	temp->pre = NULL;
	temp->next = NULL;

	body = selectDoubleLink(p, site);

	temp->next = body->next;
	temp->pre = body;
	body->next = temp;

	return p;
}

struct doubleLink* deleteDoubleLink(struct doubleLink* p, int site) {
	doubleLink* body = NULL;
	body = p;

	body = selectDoubleLink(p, site);

	body->next = body->next->next;
	body->next->pre = body;

	return p;
}	

struct doubleLink* updateDoubleLink(struct doubleLink* p, int site,int value) {
	doubleLink* body = NULL;
	body = p;

	body = selectDoubleLink(p, site);

	body->next->data = value;

	return p;
}

struct doubleLink* selectDoubleLink(struct doubleLink* p, int site) {
	doubleLink* temp = p;
	int i;
	for (i = 1; i < site; i++)
	{
		if (temp == NULL) {
			printf("查詢失敗");
			return p;
		}
		temp = temp->next;
	}
	return temp;
}



void display(doubleLink* head) {
	doubleLink* temp = head;
	while (temp) {
		if (temp->next == NULL) {
			printf("%dn", temp->data);
		}
		else {
			printf("%d->", temp->data);
		}
		temp = temp->next;
	}
}

 雙向連結串列實現貪吃蛇

貪吃蛇範例展示:

思想結構分析:

1.我們知道,雙向連結串列中各個節點的標準構成是一個資料域和 2 個指標域,但對於實現貪吃蛇遊戲來說,由於各個節點的位置是隨貪吃蛇的移動而變化的,因此連結串列中的各節點還需要隨時進行定位。
在一個二維畫面中,定義一個節點的位置,至少需要所在的行號和列號這 2 個資料。由此,我們可以得出構成貪吃蛇的雙向連結串列中各節點的構成:

//建立表示蛇各個節點的結構體
typedef struct SnakeNode {
    int x, y;//記錄節點所在的行和列
    struct SnakeNode *pre;//指向前驅節點的指標
    struct SnakeNode *next;//指向後續節點的指標
}Node, *pNode;

2.貪吃蛇的移動,本質上就是對連結串列中各個節點的重新定位。換句話說,除非貪吃蛇吃到食物,否則無論怎樣移動,都不會對雙向連結串列的整個結構(節點數)產生影響,唯一受影響的就只是各個節點中 (x,y) 這對定位資料。

由此,我們可以試著設計出實現貪吃蛇移動的功能函數,本節所用的實現思想分為 2 步:

  • 從蛇尾(雙向連結串列尾節點)開始,移動向前遍歷,過程中依次將當前節點的 (x,y) 修改為前驅節點的 (x,y),由此可實現整個蛇身(除首元節點外的其它所有節點)的向前移動;
  • 接收使用者輸入的移動指令,根據使用者指示貪吃蛇向左、向右、向上還是向下移動,首元節點中的 (x,y) 分別做 x-1、x+1、y-1 和 y+1 運算。

 如下所示,move() 函數就實現了貪吃蛇的移動:

//貪吃蛇移動的過程,即連結串列中所有節點從尾結點開始逐個向前移動一個位置
bool Move(pNode pHead, char key) {
    bool game_over = false;
    pNode pt = pTail;
    while (pt != pHead) { // 每個節點依次向前完成蛇的移動
        pt->x = pt->pre->x;
        pt->y = pt->pre->y;
        pt = pt->pre;
    }
    switch (key) {
        case'd': {
            pHead->x += 1;
            if (pHead->x >= ROW)
                game_over = true;
            break;
        }
        case'a': {
            pHead->x -= 1;
            if (pHead->x < 0)
                game_over = true;
            break;
        }
        case's': {
            pHead->y += 1;
            if (pHead->y >= COL)
                game_over = true;
            break;
        }
        case'w': {
            pHead->y -= 1;
            if (pHead->y < 0)
                game_over = true;;
            break;
        }
    }
    if (SnakeDeath(pHead))
        game_over = true;
    return game_over;
}

注意,此段程式碼中還呼叫了 SnakeDeath() 函數,此函數用於判斷貪吃蛇移動時是否撞牆、撞自身,如果是則遊戲結束。

3. 當貪吃蛇吃到食物時,貪吃蛇需要增加一截,其本質也就是雙向連結串列增加一個節點。前面章節中,已經詳細介紹瞭如何在雙向連結串列中增加一個節點,因此實現這個功能唯一的難點在於:如何為該節點初始化 (x,y)?
本節所設計的貪吃蛇遊戲,針對此問題,提供了最簡單的解決方案,就是不對新節點 x 和 y 做初始化。要知道,貪吃蛇是時刻移動的,而在上面的 move() 函數中,會時刻修正貪吃蛇每個節點的位置,因此當為雙向連結串列新增新節點後,只要貪吃蛇移動一步,新節點的位置就會自行更正。
也就是說,貪吃蛇吃到食物的實現,就僅是給雙向連結串列新增一個新節點。如下即為實現此功能的程式碼:

//建立表示食物的結構體,其中只需要記錄其所在的行和列
typedef struct Food {
    int x;
    int y;
}Food, *pFood;
//吃食物,等同於連結串列中新增一個節點
pNode EatFood(pNode pHead, pFood pFood) {
    pNode p_add = NULL, pt = NULL;
    if (pFood->x == pHead->x&&pFood->y == pHead->y) {
        p_add = (pNode)malloc(sizeof(Node));
        score++;
        pTail->next = p_add;
        p_add->pre = pTail;
        p_add->next = NULL;
        pTail = p_add;
        // 檢查食物是否出現在蛇身的位置上
        do {
            *pFood = CreateFood();
        } while (FoodInSnake(pHead, pFood));
    }
    return pHead;
}

其中,Food 結構體用來表示食物,其內部僅包含能夠定位食物位置的 (x,y) 即可。另外,此段程式碼中,還呼叫了 FoodeInSnake() 函數,由於食物的位置是隨機的,因此極有可能會和貪吃蛇重合,所以此函數的功能就是:如果重合,就重新生成食物。

FoodInSnake() 函數的實現很簡單,這裡不再贅述:

//判斷食物的出現位置是否和蛇身重合
bool FoodInSnake(pNode pHead, pFood pFood) {
    pNode pt = NULL;
    for (pt = pHead; pt != NULL; pt = pt->next) {
        if (pFood->x == pt->x&&pFood->y == pt->y)
            return true;
    }
    return false;
}

4.貪吃蛇遊戲介面的顯示,最簡單的製作方法就是:貪吃蛇每移動一次,都清除螢幕並重新生成一次。這樣實現的問題在於,如果貪吃蛇的移動速度過快,則整個介面在渲染的同時,會摻雜著遊標,並且螢幕介面會頻繁閃動。

因此,在渲染介面時,有必要將遊標隱藏起來,這需要用到<windows.h>標頭檔案,實現程式碼如下:

// 隱藏遊標
void gotoxy(int x, int y) {
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD pos;
    pos.X = x;
    pos.Y = y;
    SetConsoleCursorPosition(handle, pos);
}
void HideCursor() {
    CONSOLE_CURSOR_INFO cursor_info = { 1, 0 };
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

同時,為了給整個介面渲染上顏色,也需要引入<windows.h>標頭檔案,並使用如下函數:

void color(int m) {
    HANDLE consolehend;
    consolehend = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(consolehend, m);
}

5.需要注意的一點是,由此結束後,一定要手動釋放雙向連結串列佔用的堆空間:

//退出遊戲前,手動銷燬連結串列中各個節點
void ExitGame(pNode *pHead)
{
    pNode p_delete = NULL, p_head = NULL;
    while (*pHead != NULL) {
        p_head = (*pHead)->next;
        if (p_head != NULL)
            p_head->pre = NULL;
        p_delete = *pHead;
        free(p_delete);
        p_delete = NULL;
        *pHead = p_head;
    }
}

迴圈連結串列

無論是靜態連結串列還是動態連結串列,有時在解決具體問題時,需要我們對其結構進行稍微地調整。比如,可以把連結串列的兩頭連線,使其成為了一個環狀連結串列,通常稱為迴圈連結串列。

只需要將表中最後一個結點的指標指向頭結點,連結串列就能成環兒

需要注意的是,雖然迴圈連結串列成環狀,但本質上還是連結串列,因此在迴圈連結串列中,依然能夠找到頭指標和首元節點等。迴圈連結串列和普通連結串列相比,唯一的不同就是迴圈連結串列首尾相連,其他都完全一樣。

迴圈連結串列實現約瑟夫環

約瑟夫環問題,是一個經典的迴圈連結串列問題,題意是:已知 n 個人(分別用編號 1,2,3,…,n 表示)圍坐在一張圓桌周圍,從編號為 k 的人開始順時針報數,數到 m 的那個人出列;他的下一個人又從 1 開始,還是順時針開始報數,數到 m 的那個人又出列;依次重複下去,直到圓桌上剩餘一個人。

如圖 2 所示,假設此時圓周周圍有 5 個人,要求從編號為 3 的人開始順時針數數,數到 2 的那個人出列:

實踐專案之俄羅斯輪盤賭小遊戲

俄羅斯輪盤是一個殘忍的遊戲,具體規則為:遊戲的道具是一把左輪手槍,其規則也很簡單:在左輪手槍中的 6 個彈槽中隨意放入一顆或者多顆子彈,在任意旋轉轉輪之後,關上轉輪。遊戲的參加者輪流把手槍對著自己,扣動扳機:中槍或是怯場,即為輸的一方;堅持到最後的即為勝者。
本節實踐專案同輪盤賭類似,遊戲規則:n 個參加者排成一個環,每次由主持向左輪手槍中裝一顆子彈,並隨機轉動關上轉輪,遊戲從第一個人開始,輪流拿槍;中槍者退出賭桌,退出者的下一個人作為第一人開始下一輪遊戲。直至最後剩餘一個人,即為勝者。要求:模擬輪盤賭的遊戲規則,找到遊戲的最終勝者。

俄羅斯輪盤設計思路

決類似的問題,使用線性表的順序儲存結構和鏈式儲存結構都能實現,根據遊戲規則,在使用鏈式儲存結構時只需使用迴圈連結串列即可輕鬆解決問題。

順序儲存結構模擬輪盤
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//連結串列節點單元 
typedef struct GameMan{
    int Man;
    struct GameMan * next;
}GameMan;

//建立遊戲人員迴圈連結串列
void InitGameManLine(GameMan **GameMans,int PersonNum){
    *GameMans=(GameMan *)malloc(sizeof(GameMan));//頭指標指向首元節點 
    //節點初始化 
	(*GameMans)->next=NULL;
    (*GameMans)->Man=1;
    GameMan *tail=*GameMans;//指向連結串列尾 
    for (int i=2;i<=PersonNum;i++){
        GameMan *newnode=(GameMan *)malloc(sizeof(GameMan));//申請新節點 
        //節點初始化 
		newnode->next=NULL;
        newnode->Man=i;
        tail->next=newnode;//將新節點連結到連結串列尾 
        tail=tail->next;//移動tail到尾指標 
    }
    tail->next=*GameMans;//將連結串列成環
}

//輸出連結串列中所有遊戲成員 
void display(GameMan *GameMans){
    GameMan *temp=GameMans;
    while (temp->next!=GameMans){
        printf("%d ",temp->Man);
        temp=temp->next;
    }
    printf("%dnn",temp->Man);
}

int main() {
    GameMan *GameMans=NULL;//遊戲成員連結串列頭指標
	int round=1; 
    int PersonNum;
	int BulletPos;
//	srand((int)time(0));//使用當前時間作為rand()函數的亂數的種子 
	srand((unsigned) time(NULL));
	printf("請輸入本次遊戲人數(<100): ");
    scanf("%d",&PersonNum);
    printf("n為編號為 1-%d 的遊戲人員分配位置!nn",PersonNum);
    InitGameManLine(&GameMans,PersonNum);
    GameMan* lineNext=GameMans;//用於記錄每輪開始的位置
    //僅當連結串列中只含有一個結點時,即頭結點時,退出迴圈
    while(GameMans->next!=GameMans){
        BulletPos=rand()%6+1;
        printf("第 %d 輪遊戲,從編號為 %d 的人開始,槍在第 %d 次扣動扳機時會響!nn",round,lineNext->Man,BulletPos);
        GameMan *temp=lineNext;
        //遍歷迴圈連結串列,找到將要刪除結點的上一個結點
        for (int i=1;i<BulletPos-1;i++){
            temp=temp->next;
        }
        //如果子彈位置BulletPos==1,則要找到當前節點的上一節點 
        if(BulletPos==1){
        	while(temp->next!=lineNext){
	        	temp=temp->next;
	        }
        } 
        printf("編號為 %d 的賭徒退出賭博,剩餘賭徒編號依次為:",temp->next->Man);
        //將要刪除結點從連結串列中刪除,並釋放其佔用空間
		GameMan * del=temp->next;//記錄刪除節點 
        temp->next=temp->next->next;//從連結串列中移除該節點 
        if(del==GameMans)GameMans=GameMans->next;//如果刪除的是頭節點,將頭節點的下一節點作為頭節點 
        free(del);//釋放del節點 
        display(GameMans);
        //下一輪開始的位置
        lineNext=temp->next;
        round++;//回合次數加一
    }
    printf("最終勝利的遊戲人員編號是:%d nn",GameMans->Man);
    return 0;
}

 

執行結果範例:

 連結串列實現俄羅斯輪盤
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

//連結串列節點單元 
typedef struct GameMan{
    int Man;
    struct GameMan * next;
}GameMan;

//建立遊戲人員迴圈連結串列
void InitGameManLine(GameMan **GameMans,int PersonNum){
    *GameMans=(GameMan *)malloc(sizeof(GameMan));//頭指標指向首元節點 
    //節點初始化 
	(*GameMans)->next=NULL;
    (*GameMans)->Man=1;
    GameMan *tail=*GameMans;//指向連結串列尾 
    for (int i=2;i<=PersonNum;i++){
        GameMan *newnode=(GameMan *)malloc(sizeof(GameMan));//申請新節點 
        //節點初始化 
		newnode->next=NULL;
        newnode->Man=i;
        tail->next=newnode;//將新節點連結到連結串列尾 
        tail=tail->next;//移動tail到尾指標 
    }
    tail->next=*GameMans;//將連結串列成環
}

//輸出連結串列中所有遊戲成員 
void display(GameMan *GameMans){
    GameMan *temp=GameMans;
    while (temp->next!=GameMans){
        printf("%d ",temp->Man);
        temp=temp->next;
    }
    printf("%dnn",temp->Man);
}

int main() {
    GameMan *GameMans=NULL;//遊戲成員連結串列頭指標
	int round=1; 
    int PersonNum;
	int BulletPos;
//	srand((int)time(0));//使用當前時間作為rand()函數的亂數的種子 
	srand((unsigned) time(NULL));
	printf("請輸入本次遊戲人數(<100): ");
    scanf("%d",&PersonNum);
    printf("n為編號為 1-%d 的遊戲人員分配位置!nn",PersonNum);
    InitGameManLine(&GameMans,PersonNum);
    GameMan* lineNext=GameMans;//用於記錄每輪開始的位置
    //僅當連結串列中只含有一個結點時,即頭結點時,退出迴圈
    while(GameMans->next!=GameMans){
        BulletPos=rand()%6+1;
        printf("第 %d 輪遊戲,從編號為 %d 的人開始,槍在第 %d 次扣動扳機時會響!nn",round,lineNext->Man,BulletPos);
        GameMan *temp=lineNext;
        //遍歷迴圈連結串列,找到將要刪除結點的上一個結點
        for (int i=1;i<BulletPos-1;i++){
            temp=temp->next;
        }
        //如果子彈位置BulletPos==1,則要找到當前節點的上一節點 
        if(BulletPos==1){
        	while(temp->next!=lineNext){
	        	temp=temp->next;
	        }
        } 
        printf("編號為 %d 的賭徒退出賭博,剩餘賭徒編號依次為:",temp->next->Man);
        //將要刪除結點從連結串列中刪除,並釋放其佔用空間
		GameMan * del=temp->next;//記錄刪除節點 
        temp->next=temp->next->next;//從連結串列中移除該節點 
        if(del==GameMans)GameMans=GameMans->next;//如果刪除的是頭節點,將頭節點的下一節點作為頭節點 
        free(del);//釋放del節點 
        display(GameMans);
        //下一輪開始的位置
        lineNext=temp->next;
        round++;//回合次數加一
    }
    printf("最終勝利的遊戲人員編號是:%d nn",GameMans->Man);
    return 0;
}

 到此這篇關於C語言單雙線性及迴圈連結串列與範例的文章就介紹到這了,更多相關C語言連結串列內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com