1. 程式人生 > >通用單向連結串列的實現(附使用例子)

通用單向連結串列的實現(附使用例子)

當我開啟csdn,發現我有第一個粉絲了,激動得差點大喊“Hello World”,整晚都很開心,哈哈,為了保住我唯一的粉絲,我決定,以後勤快一點。今天就先出一篇通用連結串列實現的部落格,只包含了一個頭檔案(.h)和一個原始檔(.c),可以很方便地新增到你們的專案中。

list.h:

#ifndef __LIST_H__
#define __LIST_H__

#define LIST_NO_MEM  -2
#define LIST_ERROR   -1
#define LIST_SUCCESS  0

typedef struct _node node_t;
/*node*/
struct _node{
    node_t *next;       //next node
    void *element;      //pointer of elt
};

/*list*/
typedef struct _list{
    int nb_elt;        //number of element of list
    node_t *node;      //head pointer
}list_t;

/*initialise the list,the list shouldn't be null*/
int list_init(list_t *list);

/*get the size of a list of element*/
int list_size(const list_t *list);

/*add a element to list*/
int list_add(list_t *list,void *element,int pos);

/*get an element from a list*/
void *list_get(const list_t *list,int pos);

/*remove an element from a list*/
int list_remove(list_t *list,int pos);

#endif

list.c

#include "list.h"
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
int list_init(list_t *list)
{
    if(list == NULL)
        return LIST_ERROR;
    memset(list,0,sizeof(list_t));
    return LIST_SUCCESS;
}

int list_size(const list_t *list)
{
    if(list == NULL)
        return LIST_ERROR;
    return list->nb_elt;
}

/*index start from 0*/
int list_add(list_t *list,void *element,int pos)
{
    int i = 0;
    node_t *new_node = NULL;
    node_t *node = NULL;
    if(list == NULL)
        return LIST_ERROR;
    
    new_node = (node_t*)malloc(sizeof(node_t));
    if(new_node == NULL)
        return LIST_NO_MEM;
    memset(new_node,0,sizeof(node_t));
    new_node->element = element;

    if(list->nb_elt == 0)
    {
        new_node->next = NULL;
        list->node = new_node;
        list->nb_elt++;
        return list->nb_elt;
    }
    if(pos < 0 || pos >= list->nb_elt)
        pos = list->nb_elt;

    node = list->node;
    
    if(pos == 0)
    {
        new_node->next = list->node;
        list->node = new_node;
        list->nb_elt++;
        return list->nb_elt;
    }

    while(i + 1 < pos)
    {
        i++;
        node = node->next;
    }
    if(pos == list->nb_elt)
    {
        new_node->next = NULL;
        node->next = new_node;
        list->nb_elt++;
        return list->nb_elt;
    }
    new_node->next = node->next;
    node->next->next = new_node;
    list->nb_elt++;
    
    return list->nb_elt;
}

void *list_get(const list_t *list,int pos)
{
    int i = 0;
    node_t *node = NULL;
    if(list == NULL)
        return NULL;
    if(pos < 0 || pos >= list->nb_elt)
        return NULL;
    node = list->node;
    while(i < pos)
    {
        i++;
        node = node->next;
    }
    
    return node->element;
}

int list_remove(list_t *list,int pos)
{
    int i = 0;
    node_t *node = NULL;
    node_t *del_node = NULL;
    if(list == NULL)
        return LIST_ERROR;
    if(pos < 0 || pos >= list->nb_elt)
        return LIST_ERROR;
    node = list->node;
    /*special case*/
    if(pos == 0)
    {
        list->node = node->next;
        list->nb_elt--;
        free(node);
        return LIST_SUCCESS;
    }

    while(pos > i + 1)
    {
        i++;
        node = node->next;
    }
    del_node = node->next;
    node->next = node->next->next;
    list->nb_elt--;

    free(del_node);
    return LIST_SUCCESS;
    
}
#include <stdlib.h>
#include <stdio.h>
#include "list.h"


int main()
{
	int size = 0;
	int i = 0;
	list_t list;
	char *elt;
	list_init(&list);
	char arr1[5] = "hello";
	char arr2[5] = "world";
	char arr3[32] = "i'm pumpkin monkey";
	list_add(&list,(void*)arr1,-1);
	list_add(&list,(void*)arr2,-1);
	list_add(&list,(void*)arr3,-1);
	printf("list size:%d\n",list_size(&list));
	list_remove(&list,1);
	size = list_size(&list);
	printf("list size:%d\n",size);
	for(i = 0;i<size;i++)
	{
		elt = (char*)list_get(&list,i);
		if(elt == NULL)
		{
			printf("get null node\n");
		}
		printf("elment :%s\n",elt);
	}
	return 0;
}

以上就是最基本的通用單向連結串列的實現,包含了建立、新增、刪除、長度、獲取等最常用的基本功能。還可以包含克隆,下個元素、等其他功能,可根據需要自己新增

實際上鍊表並不複雜,但是很多人還是沒法寫好,主要還是特殊情況(邊界條件)考慮得不夠充分。不僅是在連結串列,在任何一個函式的實現裡,都必須要考慮特殊場景。對於連結串列來說,其實需要注意的也就那幾個點,目標節點(pos)是否存在、連結串列長度為0和pos剛好等於連結串列長度、輸入連結串列是否有效、迴圈條件。

大家可以考慮加個頭結點:

資料結構中,在單鏈表的開始結點之前附設一個型別相同的結點,稱之為頭結點。頭結點的資料域可以不儲存任何資訊,頭結點的指標域儲存指向開始結點的指標(即第一個元素結點的儲存位置)。

頭結點作用:

1、防止單鏈表是空的而設的.當連結串列為空的時候,帶頭結點的頭指標就指向頭結點.如果當連結串列為空的時候,單鏈表沒有帶頭結點,那麼它的頭指標就為NULL.

2、是為了方便單鏈表的特殊操作,插入在表頭或者刪除第一個結點.這樣就保持了單鏈表操作的統一性!

3、單鏈表加上頭結點之後,無論單鏈表是否為空,頭指標始終指向頭結點,因此空表和非空表的處理也統一了,方便了單鏈表的操作,也減少了程式的複雜性和出現bug的機會。

4、對單鏈表的多數操作應明確對哪個結點以及該結點的前驅。不帶頭結點的連結串列對首元結點、中間結點分別處理等;而帶頭結點的連結串列因為有頭結點,首元結點、中間結點的操作相同 ,從而減少分支,使演算法變得簡單 ,流程清晰。對單鏈表進行插入、刪除操作時,如果在首元結點之前插入或刪除的是首元結點,不帶頭結點的單鏈表需改變頭指標的值,在C 演算法的函式形參表中頭指標一般使用指標的指標(在C+ +中使用引用 &);而帶頭結點的單鏈表不需改變頭指標的值,函式引數表中頭結點使用指標變數即可。