1.3 数据结构之 链表

编程总结

在刷题之前需要反复练习的编程技巧,尤其是手写各类数据结构实现,它们好比就是全真教的上乘武功

单链表中的每个结点不仅包含值,还包含链接到下一个结点的引用字段。通过这种方式,单链表将所有结点按顺序组织起来

手写完成后,我们针对性的进行练习,练习题如下:
在这里插入图片描述

1. 添加结点

在这里插入图片描述
在这里插入图片描述

2. 删除结点

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3. 设计链表

在这里插入图片描述
在这里插入图片描述

-------------------------------------分割线---------------------------------------

4. 实战手法分析

手法1:删除结点函数的书写,其中

	if (index == 0) {        // 删除第0个点
		tmp->next = tmp->next->next;
		tmp->next = NULL;
		tmp->next->val  = 0;
		free(tmp->next);
		return ;
	}

这一块并没有判断 tmp->next->next是否存在,所以导致程序出现异常 NULL->next
在这里插入图片描述
手法2:Free结点函数的书写,其中

/*  head  -> node1 -> node2 -> node3 */
void myLinkedListFree(MyLinkedList *obj) 
{
	MyLinkedList *tmp = obj;
	MyLinkedList *freeNode;

	while (tmp) {
  		freeNode = tmp;
		tmp = tmp->next;
		freeNode->next = NULL;
		freeNode->val = 0;
		free(freeNode);
	}
	free(obj);  // 释放了两次指针
	printf("7\n");
}

执行时出现了下面的红框的log :
在这里插入图片描述在这里插入图片描述
起初没有细看这里面的log,出现了double free. 而是有个技巧,通过加 printf,找到出错的代码行,再进一步定位分析,也是一种方法.

------------------------------------分割线----------------------------------

#include <stdio.h>
#include <string.h>
#include <stdlib.h> 
#include <math.h>

// 我有一个struct MyLinkedList_t, 它的别名是MyLinkedList.
typedef struct List {
    int val;
    struct List *next; // 此处不能写 MyLinkedList,此时还不能识别该别名.
} List;

// 在此处初始化数据结构
List *ListCreate() {
    // 在链表的第一个结点之前会额外增设一个结点,头结点
    List *head = (List *)malloc(sizeof(List));
    if (head == NULL) {
        return NULL;
    }
    head->next = NULL;
    head->val  = 0; // 头结点的val用来指示链表长度

    return head;
}

// 获取链表中第index个节点的索引值。如果索引无效,则返回-1
int ListGet(List *obj, int index)
{
    int   cnt = 0;
    List *tmp = obj->next; // 手法: 因为我们是以首元结点开始index为0,所以要obj->next以首元开始, 而不是 List *tmp = obj; 

    while (tmp) {
        if (index == cnt) {
            return tmp->val;
        }
        cnt++;
        tmp = tmp->next;
    }

    return -1;
}

// 首元结点:链表中第一个元素所在的结点,头插法 
/* 在链表的第一个元素之前添加值为val的节点。插入后,新节点将成为链表的第一个节点 */
/* obj -> node1 -> node2 */
/* obj -> newNode  -> node1 -> node2 */
void AddNodeAtHead(List *obj, int val)
{
    List *newNode = (List *)malloc(sizeof(List));
    if (newNode == NULL) {
        return ;
    }
    obj->val++; // 记录链表长度

    newNode->val  = val;
    newNode->next = obj->next;
    obj->next = newNode;
}

//  将值为val的节点附加到链表的最后一个元素 尾插法
/*  head  -> node1 -> node2 */
/*  head  -> node1 -> node2 -> tail */
void AddNodeAtTail(List *obj, int val) 
{
    List *tail = (List *)malloc(sizeof(List));
    List *tmp  = obj;

    if (tail == NULL) {
        return;
    }
    obj->val++; // 记录链表长度

    tail->val  = val;
    tail->next = NULL;
    while (tmp->next) {
        tmp = tmp->next;
    }
    tmp->next = tail;
}


/* 在链表的索引节点之前添加一个值为val的节点。如果索引等于链表的长度,
   则节点将附加到链表的末尾。如果索引大于长度,则不会插入节点 */
/* obj -> node1 -> node2 */
/* obj -> node1 -> newNode -> node2 */
void AddNodeAtIndex(List *obj, int index, int val)
{
    int cnt = 0;

    if (index == obj->val) {
        AddNodeAtTail(obj, val);
        return ;
    }
    else if (index > obj->val) {
        return ;
    }
    List *tmp     = obj;
    List *newNode = (List *)malloc(sizeof(List));
    newNode->val = val;
    while (tmp) {
        if (cnt == index) {
            break;
        }
        tmp = tmp->next;
        cnt++;
    }
    obj->val++; // 记录链表长度

    newNode->next = tmp->next;
    tmp->next = newNode;

    return ;
}


/* 如果索引有效,请删除链表中的第index个节点 */
/*  head  -> node1 -> node2 -> node3 */
/*  head  -> node1 -> node3          */
void DeleteAtIndex(List *obj, int index) 
{
    List *node = NULL;
    List *tmp = obj;
    int cnt = 0;

    while (tmp) {
        if (cnt == index) {
            break;
        }
        cnt++;
        tmp = tmp->next;
    }
    if (tmp->next == NULL) { // 删除的是最后的NULL,直接返回,手法
        return;
    }
    obj->val--;
    node = tmp->next;
    tmp->next = node->next;
    free(node);
}


/*  head  -> node1 -> node2 -> node3 */
void ListFree(List *obj) 
{
    List *tmp = obj;
    List *freeNode;

    while (tmp) {  // 释放头结点->首元结点内的
        freeNode = tmp;
        tmp = tmp->next;
        freeNode->next = NULL;
        freeNode->val = 0;
        free(freeNode);
    }
}

int main(void)
{
    List *obj = ListCreate();
    int val = 1;
    int index = 0;

    AddNodeAtHead(obj,  7);
    AddNodeAtHead(obj,  2);
    AddNodeAtHead(obj,  1);
    AddNodeAtIndex(obj, 3, 0);
    DeleteAtIndex(obj,  2);
    AddNodeAtHead(obj,  6);
    AddNodeAtTail(obj,  4);
    val = ListGet(obj,  4);
    AddNodeAtHead(obj,  4);
    AddNodeAtIndex(obj, 5, 0);
    AddNodeAtHead(obj,  6);
    ListFree(obj);
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值