1. 程式人生 > >單鏈表的插入(頭插、尾插、固定點插)刪除(頭刪、尾刪、按值刪一個,按值刪所有)

單鏈表的插入(頭插、尾插、固定點插)刪除(頭刪、尾刪、按值刪一個,按值刪所有)

連結串列的插入:主要包含三種,頭插、尾插、固定位置插。

頭插:首先建立新的結點,然後修改指標。讓新結點的pNext = 頭結點,再讓頭結點->新結點

尾插:先找到最後一個結點,並把它記錄下來,再把最後一個結點的pNext域指向新的結點

固定位置插: 給定結點插入,插入到結點前(建立新結點,找到給定結點pPos的前一個結點cur,使得結點cur的pNext = 新結點;新結點的pNext = 該結點的pNext->pNext

主要的程式碼如下所示:

// 頭部插入(先建立新結點,然後修改指標.新結點的pNext = 頭結點,再讓頭結點->新結點)
void SListPushFront(SListNode **ppFirst, DataType data)
{
	SListNode *p;//新結點p
	SListNode *p1;//用來遍歷連結串列(開始位於頭結點處)
	p = _CreateNode(data);
	p1 = *ppFirst;//空連結串列的話,直接讓新結點做頭結點;有一個結點的話,和正常情況相同
	p ->pNext = p1;
    *ppFirst = p;

}

// 尾部插入(先找到最後一個結點,並把它記錄下來,再把最後一個結點的pNext域指向新的結點)
void SListPushBack(SListNode** ppFirst, DataType data)
{
	SListNode *p;//新結點p
	SListNode *p1; 
	p = _CreateNode(data); 
	p1 = *ppFirst;//用來遍歷連結串列(開始位於頭結點處)
	//空連結串列
	if( (*ppFirst) == NULL ){
		*ppFirst = p;
		return;
	} else{
		for(p1 = *ppFirst;p1->pNext != NULL;p1 = p1->pNext)
	       {

        	}
	  p1 ->pNext = p;//此時p1為最後一個結點
	}

}



// 給定結點插入,插入到結點前(建立新結點,找到給定結點pPos的前一個結點cur,使得結點cur的pNext = 新結點;
//新結點的pNext = 該結點的pNext->pNext)
void SListInsert(SListNode **ppFirst,SListNode *pPos, DataType data)
{
	SListNode *cur;//宣告結點p,用來記錄頭結點
	SListNode *p1;
	assert(&pPos);
	assert(*ppFirst);
    cur = *ppFirst;
	//在結點前做插入(結點pos肯定在連結串列中&&pos不是空)
	if(pPos == NULL&&(*ppFirst) == NULL)
	{//非法
        return;
	}
	//若考慮pPos是第一個結點的情況下,無法找到前一個結點,陷入死迴圈.此時應該直接頭插
	else if(pPos == *ppFirst)
	{
	    SListPushFront(&ppFirst,data);
	    return;//記得return 否則下邊加上else
	}
	//怎麼找pos的前一個結點(cur->next==pos就找到了)
	else{
		while(cur ->pNext != pPos)
		{
			cur = cur ->pNext;
		}
		    p1 = _CreateNode(data);//建立新結點
			p1 ->pNext = pPos;
			cur ->pNext  = p1;
		 
	}
}

連結串列的刪除:主要包含三種,頭刪、尾刪、按結點刪、按值刪(刪第一個、刪所有)。

頭刪(先free掉第一個結點,再將頭結點後移一位)

尾刪:先尾部刪除(先找到倒數第二個結點,並記錄倒數第一個結點,再把倒數第二個結點的pNext域指向NULL;釋放掉最後一個結點

按結點刪 :  給定結點刪除(找到給定結點pPos的前一個結點cur,令cur->pNext = pPos->pNext,再free掉pPos

按值刪(刪第一個): 按值刪除,只刪遇到的第一個(查詢得到該位置cur,將cur->pNext = cur->pNext->pNext; 再free掉cur)

按值刪(刪所有):若只有一個結點,和正常情況刪除相同。只有一個結點並且第一個結點的值就是要刪除的值.將*ppFirst=NULL,再free(*ppFirst);多個結點,迴圈進行刪除。

主要的程式碼如下所示:

// 頭部刪除(先free掉第一個結點,再將頭結點後移一位)
void SListPopFront(SListNode **ppFirst)
{
	SListNode *p;//宣告結點p,用來記錄頭結點
	assert(*ppFirst);
	p = *ppFirst;//用來遍歷連結串列(開始位於頭結點處)
	//直接後移頭指標.再釋放掉頭結點,
    *ppFirst = (*ppFirst)->pNext;
	free(p);
}


// 尾部刪除(先找到倒數第二個結點,並記錄倒數第一個結點,
//再把倒數第二個結點的pNext域指向NULL;釋放掉最後一個結點)
void SListPopBack(SListNode **ppFirst)
{ 
	SListNode *p;//宣告結點p
	SListNode *p1; //記錄最後一個結點
	assert(*ppFirst);
	p = *ppFirst;//用來遍歷連結串列(開始位於頭結點處)
	//只有一個結點,直接釋放掉該結點,頭指標指向NULL.
	if(p ->pNext == NULL)
	{
        free(p);
		*ppFirst = NULL;
		return;
	}
	for(p = *ppFirst;p->pNext->pNext != NULL;p = p->pNext)
	   {
         
        }//此時p為倒數第二個結點
	p1 = p->pNext;//p1為最後一個結點
    free(p1);
	p->pNext = NULL;
}


// 給定結點刪除(找到給定結點pPos的前一個結點cur,令cur->pNext = pPos->pNext,再free掉pPos)
void SListErase(SListNode **ppFirst, SListNode *pPos)
{
	SListNode *cur;
     if(pPos == *ppFirst)
	{
	    SListPopFront(&ppFirst);
	    return;//記得return 否則下邊加上else
	}
	 cur = *ppFirst;
	 //找pos的前一個結點
	 while(cur ->pNext != pPos)
		{
			cur = cur ->pNext;
		}
	 cur ->pNext = pPos->pNext;
	 free(pPos);

}

// 按值刪除,只刪遇到的第一個(查詢得到該位置cur,將
//cur->pNext = cur->pNext->pNext; 再free掉cur) 
void SListRemove(SListNode **ppFirst, DataType data)
{
	SListNode *cur ;
    SListNode *q ;
    SListNode *temp = *ppFirst;

	if((*ppFirst)->data == data)
	{
		*ppFirst =(*ppFirst)->pNext;
		free(temp);
		return;
	}
	if((*ppFirst)->pNext->data == data)
	{
		temp = (*ppFirst)->pNext;
		(*ppFirst) ->pNext = temp->pNext;
		free(temp);
		return;
	}
	for(cur = *ppFirst; cur ->pNext->data != data; cur = cur->pNext)
	{
	  
	 }
    q = cur->pNext;
    cur->pNext = q->pNext;
	free(q);
	return;
}

// 按值刪除,刪除所有的
//只有一個結點的情況和其他正常情況
void SListRemoveAll(SListNode **ppFirst, DataType data)
{
	SListNode *cur;
	SListNode *pre;
	if(*ppFirst == NULL)
	{
		printf("連結串列無元素可刪除\n");
		return -1;
	}
    pre = *ppFirst;
    cur = *ppFirst;
	//只有一個結點並且第一個結點的值就是要刪除的值.將*ppFirst=NULL,再free(*ppFirst)
	if((*ppFirst)->data==data&&(*ppFirst)->pNext==NULL)
	{
		*ppFirst=NULL;
		free(cur);
		return;
	}

    while(cur != NULL)
	    {
		 if(cur ->data == data)
		   {
			 pre->pNext = cur ->pNext;
			 free(cur);
			 cur=pre->pNext;
			 continue;//如果找到了,釋放cur之後,此時應該cur = pre->pNext,繼續迴圈
	       }
		 pre = cur;
		 cur = cur ->pNext;//沒找到的情況,迴圈後移
	   }
}

        上述程式碼所包含的建立結點函式如下所示:

//建立新結點,結點的資料域為data,pNext域設定為空
SListNode *_CreateNode(DataType data)
{ 
    SListNode *NewNode; //宣告新結點
	NewNode = (SListNode *)malloc(sizeof(SListNode));//建立新結點
	if(NewNode == NULL)
	{
		return;
	}//建立失敗
	NewNode ->data = data;
	NewNode ->pNext = NULL;//建立成功
	
}

        上述單鏈表插入與刪除的操作中的測試程式碼如下所示:

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

int main()
{
        SListNode *result;
        SListNode *p1;
        SListInit(&p1);

        SListPushBack(&p1,1);
        SListPushBack(&p1,2);
	SListPushBack(&p1,3);
	SListPushBack(&p1,2);
	SListPushBack(&p1,5);
	SListPushBack(&p1,2);
	SListPushBack(&p1,6);
	SListPushBack(&p1,1);
	SListPushBack(&p1,2);
	SListPushBack(&p1,3);

	SListPushFront(&p1, 0);
	SListPushFront(&p1,-1);

        result = SListFind(p1,5);
	SListInsert(&p1, result, 4);

	SListPopBack(&p1);
	SListPopFront(&p1);

	result = SListFind(p1,3);
	SListErase(&p1,result);

	SListRemove(&p1, 4);
	SListRemoveAll(&p1,2);

	SListprint(p1);

	SListDestroy(&p1);
        SListprint(p1);

	system("pause");
	return 0;
}