鏈表的初步認識以及基本操作

        記得上篇順序表的博客曾經提到過鏈表,鏈表和順序表共同組成線性表,鏈表邏輯相鄰,物理上不相鄰,順序表則是邏輯和物理都相鄰,可以簡單的理解爲順序表中的數據相鄰同時保存數據的地址也相鄰,但鏈表僅僅是數據相鄰,但數據的地址不相鄰。因爲鏈表物理不相鄰這個特點所以我們在定義鏈表時除了放數據之外還要存放下一個數據的地址從而使數據連接起來。

       他的一個基礎結構爲:

      鏈表就是由若干個這樣的結構串聯起來的,和順序表一樣,我們使用的時候需要使用結構體來進行定義,還需要注意的一點是鏈表可以使用頭結點和尾結點來方便對我們鏈表進行操作,其中頭結點不保存數據,只保存第一個數據的地址,尾結點就是下一個數據局的地址爲NULL,有了頭結點和尾結點我們可以比較方便的對鏈表進行頭尾處理。

      對鏈表我們通常進行增,刪,改,查,逆序,打印,清空之類的操作,其中插入數據時我們有頭插和尾插兩種方法,顧名思義,頭插就是從頭部開始插入,這也導致了我們插入的數據是逆序的,尾插則是從後開始插入,順序的。接下來給大家寫一下鏈表的一系列操作

​
void InitList(List plist)
{
	assert(plist != NULL);
	if(plist == NULL)
	{
		return ;
	}
	plist->next = NULL;
}

//頭插
bool Insert_head(List plist,int val)//O(1)
{
	Node *p = (Node *)malloc(sizeof(Node));
	p->data = val;
	p->next = plist->next;
	plist->next = p;
	return true;
}

//尾插
bool Insert_tail(List plist,int val)//O(n)
{
	Node *p = (Node *)malloc(sizeof(Node));
	p->data = val;

	Node *q;
	for(q=plist;q->next!=NULL;q=q->next) ;//找結尾

	//將p插入在q後面
	p->next = q->next;//p->next =  NULL;
	q->next = p;

	return true;
}

//查找
Node *Srearch(List plist,int key)
{
	for(Node *p=plist->next;p!=NULL;p=p->next)
	{
		if(p->data == key)
		{
			return p;
		}
	}
	return NULL;
}

//刪除
bool Delete(List plist,int key)
{
	Node *p;
	for(p=plist;p->next!=NULL;p=p->next)
	{
		if(p->next->data == key)
		{
			Node *q = p->next;//記錄需要刪除的節點
			p->next = q->next;
			free(q);		
			return true;
		}
	}

	return false;
}

//獲取數據節點個數
int GetLength(List plist)
{
	int count = 0;
	for(Node *p=plist->next;p!=NULL;p=p->next)//遍歷所有數據節點
	{
		count++;
	}
	return count;
}

bool IsEmpty(List plist)
{
	return plist->next == NULL;
}

void Clear(List plist)
{
	Destroy(plist);
}

void Destroy(List plist)
{
	Node *p;
	while(plist->next != NULL)
	{
		p = plist->next;
		plist->next = p->next;
		free(p);
	}
	/*Node *p = plist->next;
	Node *q;

	while(p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}
	plist->next = NULL;*/
}

void Show(List plist)
{
	for(Node *p=plist->next;p!=NULL;p=p->next)//++
	{
		printf("%d ",p->data);
	}
	printf("\n");
}

//第一個數據節點的pos爲0
bool GetElem(List plist,int pos,int *rtval)
{
	if(pos<0 || pos>=GetLength(plist))
	{
		return false;
	}

	int i = 0;
	for(Node *p=plist->next;p!=NULL;p=p->next)
	{
		if(i == pos)
		{
			*rtval = p->data;
			return true;
		}
		i++;
	}

	return false;
}

//得到節點p的前驅
static Node *GetPri(List plist,Node *p)
{
	for(Node *q=plist;q->next !=NULL ;q=q->next)
	{
		if(q->next == p)
		{
			return q;
		}
	}

	return NULL;
}

//逆置
void Reverse(List plist)//考試的重點內容
{
	if(plist==NULL || plist->next==NULL || plist->next->next==NULL)
	{
		return ;
	}

	//下面的時間複雜度爲O(n),並且最好,利用頭插的思想
	Node *p = plist->next;
	Node *q;
	plist->next = NULL;//

	while(p != NULL)
	{
		q = p->next;

		p->next = plist->next;//將節點p頭插到鏈表中
		plist->next = p;

		p = q;
	}

	/*//下面的時間複雜度爲O(n)
	Node *p = plist->next;
	Node *q = p->next;
	Node *s;
	p->next = NULL; //

	while(q != NULL)
	{
		s = q->next;
		q->next = p; //反轉

		p = q;
		q = s;
	}

	plist->next = p;
	*/

	/*
	//下面方法的時間複雜度爲O(n^2)
	Node *p = plist->next;
	Node *q;
	int tmp;

	for(q=plist;q->next!=NULL;q=q->next) ;

	for(int i=0;i<GetLength(plist)/2;i++)
	{
		tmp = p->data;
		p->data = q->data;
		q->data = tmp;

		p = p->next;
		q = GetPri(plist,q);
	}
	*/
}

void Unique(List plist)
{
	Node *p;
	Node *q;
	for(p=plist->next;p!=NULL;p=p->next)
	{
		for(q=p;q->next!=NULL;q=q->next)
		{
			if(q->next->data == p->data)
			{
				Node *s = q->next;
				q->next = s->next;
				free(s);
			}
		}
	}
}

​

 以上就是我對鏈表的基本認識理解,如果哪裏出現了問題希望大家多多指教

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