其中 previous 、 current 和 new 是三个指向结点的指针, new 指向要插入的结点, previous 、 current 一前一后,在进行插入操作之前的关系为:
current = previous->next;事实上, current 指针不是必需的,只有一个 previous 也可以做到插入操作,原因就是 current = previous->next,这种情况下的核心代码变为:
new->next = previous->next; previous->next = new;但请注意,在这种情况下两句代码是有顺序的,你不能写成:
// 错误代码 previous->next = new; new->next = previous->next;我们可以从两个角度来理解为什么这两句会有顺序:
【角度一】
因为 current 指针的作用就是用来保存 previous 的直接后继结点的地址的,所以在我们断开 previous 和 current 联系后,我们仍能找到 current 及其以后的结点。“链子”就算暂时断开了,由于断开处两侧都有标记,我们也能接上去。。
但是现在没了 current 之后,一旦断开, current 及其以后的结点就消失在茫茫内存中,这就关键时刻掉链子了。
所以我们要先把 new 和 previous->next( previous 的直接后继结点)连起来,这样一来,指针 new 就保存了它所指向的及其以后的结点,
【角度二】
直接看代码,previous->next = new 执行完后 new->next = previous->next 就相当于 new->next = new ,自己指自己,这显然不正确。
总之,把核心代码理解到位,剩下的就在于如何准确的找到 previous 和 current 的位置。
指定位置插入 不带头结点我们需要考虑两种情况:
在第一个元素前插入
在其他元素前插入
/** * 指定插入位置 * p_head: 指向头指针的指针 * position: 指定位置 (1 <= position <= length + 1) * elem: 新结点的数据 */ void insert(Node **p_head, int position, int elem) { Node *new = create_node(elem); Node *current = *p_head; Node *previous = NULL; int length = get_length(*p_head); if (position < 1 || position > length + 1) { printf("插入位置不合法\n"); return; } for (int i = 0; current != NULL && i < position - 1; i++) { previous = current; current = current->next; } new->next = current; if (previous == NULL) *p_head = new; else previous->next = new; } 带头结点由于带了一个头结点,所以在第一个元素前插入和在其他元素前插入时的操作是相同的。
/** * 指定插入位置 * p_head: 指向头指针的指针 * position: 指定位置 (1 <= position <= length + 1) * elem: 新结点的数据 */ void insert(Node **p_head, int position, int elem) { Node *new = create_node(elem); Node *previous = *p_head; Node *current = previous->next; int length = get_length(*p_head); if (position < 1 || position > length + 1) { printf("插入位置不合法\n"); return; } for (int i = 0; current != NULL && i < position - 1; i++) { previous = current; current = current->next; } new->next = current; previous->next = new; } 头插法 不带头结点不带头结点的头插法,即新插入的节点始终被头指针所指向。
/** * 头插法:新插入的节点始终被头指针所指向 * p_head: 指向头指针的指针 * elem: 新结点的数据 */ void insert_at_head(Node **p_head, int elem) { Node *new = create_node(elem); new->next = *p_head; *p_head = new; } 带头结点带头结点的头插法,即新插入的结点始终为头结点的直接后继。
/** * 头插法,新结点为头结点的直接后继 * p_head: 指向头指针的指针 * elem: 新结点的数据 */ void insert_at_head(Node **p_head, int elem) { Node *new = create_node(elem); new->next = (*p_head)->next; (*p_head)->next = new; }注意:多了一个头结点,所以代码有所变化。
尾插法