c語言實現雙鏈表

  中午寫了一篇關於單鏈表的博客。好吧,我並無搜到我寫的這篇文章。但我仍是要寫下去,萬一有人看到了呢……不過,呵呵。。。node

雙鏈表和單鏈表的操做大同小異,只是多了一個前驅指針,我是這樣定義的。函數

typedef struct node
{
    int data;
    struct node * previous;
    struct node * next;
} Node;

typedef struct dlist
{
    Node * head;
} Dlist;

 與單鏈表相比,它只多了一個前驅節點。請看圖示!spa

這裏用prev表明previous,我在第一次寫雙鏈表時,previous與next到底指向哪裏糾結了半天,從圖上來看,previous好像指向了前驅的next,然而並非。設計

previous和next指向的都是節點的首地址,並非previous或者next。也就是說previous和next裏面存的是前驅節點和後繼節點的地址值。指針

爲了操做方便,咱們設計了一個頭節點。調試

void d_init(Dlist * list)
{
    list->head = make_node(INT_MIN);//建立頭節點,並賦值一個整形的最小值
}

由於雙鏈表具備前驅指針,因此咱們能夠方便的找到要操做的節點的前驅和後繼。code

插入操做,要實現順序鏈表,因此第一步仍是找到插入位置,第二步進行插入。請看圖示!blog

先作一、2步,再作三、4步,這裏主要是想先講新節點連接到鏈表上,一、2誰先並無關係。而後將3節點的後繼與5節點的前驅連接到新節點上。內存

這裏還有一種狀況,就是在最後的位置插入新節點。這樣只需將將新節點與前驅節點連接就能夠了。就是不作一、4步,先作二、再作3就能夠了。rem

if ( current->next == NULL )//在鏈表的尾端插入和中間插入不一樣
    {
        current->next = node;
        node->previous = current;
    }
    else
    {
        node->previous = current->previous; //將新節點的前驅指向當前節點的前驅
        node->next = current;//將新節點的後繼指向當前節點,因此這時一個前插方式

        current->previous->next = node;//將當前節點的前驅的後繼指向新節點
        current->previous = node;//將當前節點的前驅指向新節點
    }

 

刪除操做,仍是先找到刪除節點。而後將刪除節點從鏈表中摘出來,釋放內存。請看圖示!

因爲咱們只知道current指針,因此咱們找到3節點的後繼指針的辦法是current->previous->next。而找5節點前驅指針也是相似的,current->next->privous。而後就能夠釋放刪除節點了。

主要的操做已經講完了,接下來看源碼吧。

仍是有兩部分組成,dlist.h和dlist.c

dlist.h

#ifndef _DLIST_H
#define _DLIST_H

typedef struct node
{
    int data;
    struct node * previous;
    struct node * next;
} Node;

typedef struct dlist
{
    Node * head;
} Dlist;

void d_init(Dlist * list);//初始化
bool d_insert(Dlist * list, const int data);//插入
bool d_remove(Dlist * list, const int key);//移除
bool d_modify(Dlist * list, const int key, const int data);//修改
Node* d_find(Dlist * list, const int key);//查找
void d_treaverse(Dlist * list, void (*func)(void * pointer) );//遍歷
void d_destroy(Dlist * list);//銷燬

#endif //_DLIST_H

dlist.c

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <limits.h>
#include <stdbool.h>

#include "dlist.h"

static Node* make_node(const int data);//建立節點
static void destroy_node(void * pointer);//銷燬節點
static void treaverse_list(Node * current, void (*func)(void * pointer) );//遍歷鏈表,包括頭節點

void d_init(Dlist * list)
{
    list->head = make_node(INT_MIN);//建立頭節點,並賦值一個整形的最小值
}

bool d_insert(Dlist * list, const int data)
{
    Node * current = list->head;//當前指向頭節點
    Node * node;//新節點指針
    
    while ( current->next != NULL && current->data < data) //找到插入位置
        current = current->next;//在不是尾節點結束時,當前指針的值要大於新值,因此插入在當前節點以前

    if ( current->data == data )//若是數據重複,插入失敗
        return false;
    
    node = make_node(data);

    if ( current->next == NULL )//在鏈表的尾端插入和中間插入不一樣
    {
        current->next = node;
        node->previous = current;
    }
    else
    {
        node->previous = current->previous; //將新節點的前驅指向當前節點的前驅
        node->next = current;//將新節點的後繼指向當前節點,因此這時一個前插方式

        current->previous->next = node;//將當前節點的前驅的後繼指向新節點
        current->previous = node;//將當前節點的前驅指向新節點
    }

    return true;
}

bool d_remove(Dlist * list, const int key)
{
    Node * current = list->head;//當前指針指向頭節點

    while ( (current = current->next) != NULL && current->data < key) ;//找到關鍵元素,因爲是順序鏈表,因此不用所有遍歷所有鏈表,只要找到第一不小於關鍵key的位置

    if ( current->next == NULL && current->data == key )//刪除最後一個節點和中間節點方法不一樣
        current->previous->next = NULL;//刪除最後一個節點
    else if ( current->data == key )//刪除中間節點
    {
        current->previous->next = current->next;//當前節點的前驅的後繼指向當前節點的後繼
        current->next->previous = current->previous;//當前節點的後繼的前驅指向當前節點的前驅
    }
    else
        return false;//沒有找到關鍵字,返回false

    free(current);//釋放當前指針內存

    return true;
}

bool d_modify(Dlist * list, const int key, const int data)//修改元素,由於是有序鏈表,因此要先刪除後插入
{
    if ( d_remove(list, key) )
        if ( d_insert(list, data) )
            return true;
    
    return false;
}

Node* d_find(Dlist * list, const int key)
{
    Node * current = list->head;//當前指針指向頭節點

    while ( (current = current->next) != NULL && current->data < key );//找到關鍵字位置

    if (current->data == key)//判斷查找的元素是否存在
        return current;
    else
        return NULL;
}

void d_treaverse(Dlist * list, void (*func)(void * pointer) )
{
    treaverse_list(list->head->next, func);//遍歷,不包括頭節點
}

void d_destroy(Dlist * list)
{
    treaverse_list(list->head, destroy_node); //遍歷,銷燬鏈表
    list->head = NULL;
}

static void treaverse_list(Node * current, void (*func)(void * pointer) )//這裏使用函數指針,是爲了程序的可擴展行,本程序中,d_treaverse 和 d_destroy 函數都用該函數實現不一樣功能
{
    while ( current != NULL )//遍歷,包括頭節點
    {
        func(current);
        current = ( (Node *)current)->next; //強制類型轉換,因爲->的優先級高於(),因此用括號改變結合順序
    }
}

static void destroy_node(void * pointer)//釋放內存
{
    free((Node *)pointer);
}

static Node* make_node(const int data)//建立節點
{
    Node * node = (Node *)malloc( sizeof(Node) );
    if ( node == NULL )
    {
        exit(1);
        printf("動態分配內存失敗,程序結束.");
    }    
    node->data = data;
    node->previous = NULL;
    node->next = NULL;

    return node;
}

還有一件事忘說了,這些代碼都是在gcc下調試過的,我電腦沒裝Windows,因此就沒有實驗,不過問題應該不大,若是哪裏出現問題,請聯繫我,歡迎你們指正。。。

2016-01-10

相關文章
相關標籤/搜索