链表

171 阅读6分钟

一、 基本特征: 

内存中不连续的节点序列, 节点之间通过next指针彼此相连; 每个节点的next指针都指向下一个节点,最后一个节点的next指针为NULL.

二、基本操作: 

插入、删除、遍历

三、形式: 

单向链表, 双向链表, 单向循环链表, 双向循环链表

四、实现要点: 

追加: 将新分配节点的地址赋给原链表尾端节点的next指针

插入: 将前节点中存储的后节点地址赋给新节点的next指针,将新节点的地址赋给前节点的next指针

删除: 将前节点的next指针赋值为待删除节点的next指针, 对于单向链表而言, 寻找前节点会有一定开销

遍历: 沿着next指针依次访问链表中的各个节点

伪随机访问: 遍历+计数

范例:

A、双向链表
// .h
// 双向线性链表
#ifndef _L_H
#define _L_H

#include <sys/types.h>

// 节点
typedef struct ListNode {
    int data;  // 数据
    struct ListNode* prev;  // 前指针
    struct ListNode* next;  // 后指针
} LIST_NODE;

typedef struct List {
    LIST_NODE* head;  // 头指针
    LIST_NODE* tail;  // 尾指针
    LIST_NODE* frwd;  // 正向迭代指针
    LIST_NODE* bkwd;  // 反向迭代指针
} LIST;

// 初始化为空链表
void list_init(LIST* list);

// 释放剩余节点并恢复到初始化状态
void list_deinit(LIST* list);

// 判断是否为空
int list_empty(LIST* list);

// 追加
void list_append(LIST* list, int data);

// 前插
void list_insert(LIST* list, size_t pos, int data);

// 随机访问
int* list_at(LIST* list, size_t pos);

// 删除
void list_erase(LIST* list, size_t pos);

// 删除所有匹配数据
void list_remove(LIST* list, int data);

// 清空
void list_clear(LIST* list);

// 长度
size_t list_size(LIST* list);

// 开始正向迭代
void list_begin(LIST* list);

// 继续正向迭代
int* list_next(LIST* list);

// 获取正向迭代
int* list_current(LIST* list);

// 终止正向迭代
void list_end(LIST* list);

// 判断正向迭代是否终止
int list_done(LIST* list);

// 开始反向迭代
void list_rbegin(LIST* list);

// 继续反向迭代
int* list_rnext(LIST* list);

// 获取反向迭代
int* list_rcurrent(LIST* list);

// 终止反向迭代
void list_rend(LIST* list);

// 判断反向迭代是否终止
int list_rdone(LIST* list);

#endif

// .c
#include <stdio.h>
#include <stdlib.h>
#include "clink.h"

// 创建节点
static LIST_NODE* create_node(int data, LIST_NODE* prev, LIST_NODE* next)
{
    LIST_NODE* node = (LIST_NODE*)malloc(sizeof(LIST_NODE));
    node->data = data;
    node->prev = prev;
    node->next = next;
    return node;
}

// 销毁节点
static LIST_NODE* destroy_node(LIST_NODE* node, LIST_NODE** prev)
{
    LIST_NODE* next = node->next;
    if (prev) {
        *prev = node->prev;
    }
    free(node);
    return next;
}

// 初始化为空链表
void list_init(LIST* list)
{
    list->head = NULL;
    list->tail = NULL;
}

// 释放剩余节点并恢复到初始化状态
void list_deinit(LIST* list)
{
    while (list->head) {
        list->head = destroy_node(list->head, NULL);
    }
    list->tail = NULL;
}

// 判断是否为空
int list_empty(LIST* list)
{
    return ! list->head && ! list->tail;
}

// 追加
void list_append(LIST* list, int data)
{
    list->tail = create_node(data, list->tail, NULL);
    if (list->tail->prev) {
        list->tail->prev->next = list->tail;
    }
    else {
        list->head = list->tail;
    }
}

// 前插
void list_insert(LIST* list, size_t pos, int data)
{
    LIST_NODE* find = NULL;
    for (find = list->head; find; find = find->next) {
        if (!pos--) {
            LIST_NODE* node = create_node(data, find->prev, find);
            if (node->prev) {
                node->prev->next = node;
            }
            else {
                list->head = node;
            }
            node->next->prev = node;
            break;
        }
    }
}

// 随机访问
int* list_at(LIST* list, size_t pos)
{
    LIST_NODE* find = NULL;
    for (find = list->head; find; find = find->next)
    {
        if (!pos--) {
            break;
        }
    }
    return &find->data;
}

// 删除
void list_erase(LIST* list, size_t pos)
{
    LIST_NODE* find = NULL;
    for (find = list->head; find; find = find->next) {
        if (!pos--) {
            LIST_NODE* prev = NULL;
            LIST_NODE* next = destroy_node(find, &prev);

            if (prev) {
                prev->next = next;
            }
            else {
                list->head = next;
            }

            if (next) {
                next->prev = prev;
            }
            else {
                list->tail = prev;
            }

            return;
        }
    }
}

// 删除所有匹配数据
void list_remove(LIST* list, int data)
{
    LIST_NODE *find = NULL, *next = NULL;
    for (find = list->head; find; find = next) {
        next = find->next;
        if (find->data == data) {
            LIST_NODE* prev = NULL;
            next = destroy_node(find, &prev);

            if (prev) {
                prev->next = next;
            }
            else {
                list->head = next;
            }

            if (next) {
                next->prev = prev;
            }
            else {
                list->tail = prev;
            }
        }
    }
}

// 清空
void list_clear(LIST* list)
{
    list_deinit(list);
}

// 长度
size_t list_size(LIST* list)
{
    size_t count = 0;
    LIST_NODE* find;
    for (find = list->head; find; find = find->next) {
        ++count;
    }
    return count;
}

// 开始正向迭代
void list_begin(LIST* list)
{
    list->frwd = list->head;
}

// 继续正向迭代
int* list_next(LIST* list)
{
    int* data = &list->frwd->data;
    list->frwd = list->frwd->next;
    return data;
}

// 获取正向迭代
int* list_current(LIST* list)
{
    return &list->frwd->data;
}

// 终止正向迭代
void list_end(LIST* list)
{
    list->frwd = list->tail->next;
}

// 判断正向迭代是否终止
int list_done(LIST* list)
{
    return list->frwd == list->tail->next;
}

// 开始反向迭代
void list_rbegin(LIST* list)
{
    list->bkwd = list->tail;
}

// 继续反向迭代
int* list_rnext(LIST* list)
{
    int * data = &list->bkwd->data;
    list->bkwd = list->bkwd->prev;
    return data;
}

// 获取反向迭代
int* list_rcurrent(LIST* list)
{
    return &list->bkwd->data;
}

// 终止反向迭代
void list_rend(LIST* list)
{
    list->bkwd = list->head->prev;
}

// 判断反向迭代是否终止
int list_rdone(LIST* list)
{
    return list->bkwd == list->head->prev;
}

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

int main(void)
{
    LIST list;

    list_init(&list);

    list_append(&list, 10);
    list_append(&list, 30);
    list_append(&list, 50);

    list_insert(&list, 1, 20);
    list_insert(&list, 3, 40);

    for (list_begin(&list); !list_done(&list); list_next(&list)) {
        printf("%d ", ++*list_current(&list));
    }
    printf("\n");

    list_append(&list, 41);
    list_append(&list, 41);
    list_begin(&list);
    while (!list_done(&list)) {
        printf("%d ", *list_next(&list));
    }
    printf("\n");

    list_erase(&list, 1);
    list_remove(&list, 41);
    list_rbegin(&list);
    while (!list_rdone(&list)) {
        printf("%d ", *list_rnext(&list));
    }
    printf("\n");

    size_t pos, size = list_size(&list);
    for (pos = 0; pos < size; ++pos) {
        printf("%d ", --*list_at(&list, pos));
    }
    printf("\n");

    list_clear(&list);
    printf("%d, %zu\n", list_empty(&list), list_size(&list));

    list_deinit(&list);

    return 0;
}

/*
Output:

11 21 31 41 51 
11 21 31 41 51 41 41 
51 31 11 
10 30 50 
1, 0

*/

B、单向链表练习

(1)实现list_append()、list_size()、list_print()和list_rprint(),  分别用于单向链表的追加、测长、正向打印和反向打印

(2)实现list_reverse()函数, 用于将单向链表逆转

(3)实现list_middle()函数, 用于获取单向链表的中间值, 平均时间复杂度不得超过O(N)级

// .h
// 双向线性链表
#ifndef _L_H
#define _L_H

#include <sys/types.h>

// (1)实现list_append()、list_size()、list_print()和list_rprint(),  分别用于单向链表的追加、测长、正向打印和反向打印
// (2)实现list_reverse()函数, 用于将单向链表逆转
// (3)实现list_middle()函数, 用于获取单向链表的中间值, 平均时间复杂度不得超过O(N)级

// 节点
typedef struct ListNode {
    int data;  // 数据
    struct ListNode* next;  // 前指针
} LIST_NODE;

typedef struct List {
    LIST_NODE* head;  // 头指针
    LIST_NODE* tail;  // 尾指针
    size_t size;
} LIST;

// 初始化为空链表
void list_init(LIST* list);

// 释放剩余节点并恢复到初始化状态
void list_deinit(LIST* list);

// 判断是否为空
int list_empty(LIST* list);

// 追加
void list_append(LIST* list, int data);

// 获取节点的前指针
LIST_NODE* list_prev(LIST* list, LIST_NODE* node);

// 长度
size_t list_size(LIST* list);

// 正向打印
void list_print(LIST* list);

// 反向打印
void list_rprint(LIST* list);

void list_rprint2(LIST* list);

// 单向链表逆转
LIST list_reverse(LIST* list);

void list_reverse2(LIST* list);

// 获取单向链表中间值
void list_middle(LIST* list);

void list_middle2(LIST* list);

#endif

// .c
#include <stdio.h>
#include <stdlib.h>
#include "clink.h"

// 创建节点
static LIST_NODE* create_node(int data)
{
	LIST_NODE* node = (LIST_NODE*)malloc(sizeof(LIST_NODE));
	node->data = data;
	node->next = NULL;
	return node;
}

// 销毁节点
static LIST_NODE* destroy_node(LIST_NODE* node)
{
	LIST_NODE* next = node->next;
	free(node);
	return next;
}

// 初始化为空链表
void list_init(LIST* list)
{
	list->head = NULL;
	list->tail = NULL;
	list->size = 0;
}

// 释放剩余节点并恢复到初始化状态
void list_deinit(LIST* list)
{
	while (list->head) {
		list->head = destroy_node(list->head);
	}
	list->tail = NULL;
	list->size = 0;
}

// 判断是否为空
int list_empty(LIST* list)
{
	return ! list->size;
}

// 追加
void list_append(LIST* list, int data)
{
	LIST_NODE* node = create_node(data);
	if (list->tail) {
		list->tail->next = node;
	}
	else {
		list->head = node;
	}
	list->tail = node;

	++list->size;
}

// 获取节点的前指针
LIST_NODE* list_prev(LIST* list, LIST_NODE* node)
{
	LIST_NODE* find = NULL;
	for (find = list->head; find; find = find->next) {
		if (find->next == node) {
			break;
		}
	}
	return find;
}

// 长度
size_t list_size(LIST* list)
{
	return list->size;
}

void list_print(LIST* list)
{
	LIST_NODE* find = NULL;
	for (find = list->head; find; find = find->next) {
		printf("%d ", find->data);
	}
	printf("\n");
}

void list_rprint(LIST* list)
{
	LIST_NODE* find = NULL;
	for (find = list->tail; find; find = list_prev(list, find)) {
		printf("%d ", find->data);
	}
	printf("\n");
}

// 反向打印以参数head的目标节点为首的子链表
// 当你下一步要做的事,恰好是你正在做的事, 就可以使用递归
static void rprint(LIST_NODE* head)
{
	// 此处利用了函数栈的原理, 找到链表最后一项非空值, 然后从函数参数栈pop, 逆向打印数据
	// 函数参数栈事从右向左压栈的
	if (head) {
		rprint(head->next);
		printf("%d ", head->data);
	}
}

// 第二种方式逆向打印
void list_rprint2(LIST* list)
{
	rprint(list->head);
	printf("\n");
}

// 第三种方式逆向打印: 先正向打印, 然后把结构存到栈里, 再pop出来


// 单向链表逆转
LIST list_reverse(LIST* list)
{
	LIST newlist;
	list_init(&newlist);

    LIST_NODE* find = NULL;
    for (find = list->tail; find; find = list_prev(list, find)) {
    	list_append(&newlist, find->data);
    }

    list_deinit(list);

    return newlist;
}

// 逆转以参数node的目标节点为首的子链表
static void reverse(LIST_NODE* node)
{
	if (node && node->next) {
		reverse(node->next);
		node->next->next = node;
		node->next = NULL;
	}
}

void list_reverse2(LIST* list)
{
	reverse(list->head);
	LIST_NODE* swap = list->head;
	list->head = list->tail;
	list->tail = swap;
}

// 获取单向链表中间值
void list_middle(LIST* list)
{
    int leng = list_size(list);
    int middle;
    if (leng % 2) {
        middle = leng / 2;
    }
    else {
        middle = leng / 2 - 1;
    }

    LIST_NODE* find = NULL;
    for (find = list->head; find; find = find->next) {
        if (!middle--) {
            printf("中间值 = %d \n", find->data);
        }
    }
}

// 定义两个指针, 一个每次走两格, 一个每次走一格, 快指针到头的时候, 就是慢指针到中间的时候
void list_middle2(LIST* list)
{
    LIST_NODE *fast = NULL, *slow = NULL;

    for (fast = slow = list->head; fast->next && fast->next->next; fast = fast->next->next) {
        slow = slow->next;
    }
    printf("第二种方式获取 中间值 =  %d \n", slow->data);
}

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

void reverseTest(void);

int main(void)
{
    LIST list;

    list_init(&list);

    list_append(&list, 10);
    list_append(&list, 30);
    list_append(&list, 50);

    list_print(&list);

    list_append(&list, 41);
    list_append(&list, 41);

    printf("size = %zu\n", list_size(&list));

    list_rprint(&list);

    list_rprint2(&list);

    list = list_reverse(&list);
    list_print(&list);

    list_deinit(&list);

    reverseTest();

    return 0;
}

void reverseTest(void)
{
    LIST list;
    list_init(&list);
    int i;
    for (i = 0; i < 11; ++i) {
        list_append(&list, i);
    }
    list_print(&list);

    list_reverse2(&list);
    list_print(&list);

    list_middle(&list);

    list_middle2(&list);

    list_deinit(&list);
}
/*
Output:

10 30 50 
size = 5
41 41 50 30 10 
41 41 50 30 10 
41 41 50 30 10 
0 1 2 3 4 5 6 7 8 9 10 
10 9 8 7 6 5 4 3 2 1 0 
中间值 = 5 
第二种方式获取 中间值 =  5 

*/