1.在上一节中,我们完成了最基本的操作——链表的创建,现在我们来做一下其他的操作。这也是链表相对于数组最大的优势所在——数据的插入与删除。(还是基于单链表)
2.在链表中插入一个数据。插入数据也就是插入一个节点.现在我们来考虑如何插入,首先需要知道插入的位置,这个由外界数据给出。当我们知道把数据插入到链表中的哪个位置之后,就开始着手如何插入。我们从链表的结构出发来思考,首先链表是用指针链接起来的,指针的作用就像一根绳子,所有的节点都在这个绳子上。那么,如果我要插入一个数据,(除非是在头部或者尾部插入)一定要先把绳子剪断,然后把节点连接上去。其实,我们在上一节已经提到了,我们当时对头节点做了处理,使的第一个节点有指针指向它,这让所有的节点的结构统一了起来。所以我们这里的操作就可以对所有节点一视同仁。那么问题来了,绳子被剪断以后把节点加在中间,到底是先连接绳子的前半部分还是先连接绳子的后半部分,这是一个问题。我们来分析一下。如果先链接前半部分,对应的代码逻辑就是,先让待插入位置的前一个节点的指针指向新的节点,再链接后半部分,那么这个时候问题就来了,后半部分吗在哪?当绳子断开以后,后半部分与前半部分已经失去联系,这个时候再通过前半部分来寻找下一个节点显示是不可能的。所以先连接前半部分不可行。如果先连接后半部分,也就是让新节点的指针指向当前节点的下一个节点(这句话有点绕),然后再让当前节点指向新的节点。这是可行的。分析之后,具体的代码实现:
//linknode是指向结构体的指针,typedef linklist* linknode
linknode insert_by_position(linknode head, int pos, int data) {
linknode p = head;
linknode newnode = new linklist(data);
int num = 1;
while (p!=NULL&&num<pos)
{
p = p->next;
++num;
}
newnode->next = p->next;//原来的next连接到新的节点
p->next = newnode;//原来的pos位置变成现在的newnode
return head;
}
这样就实现了新节点的插入。
3.数据的删除。也就是节点的删除,通过添加我们可以分析到,删除也只需要知道待删除节点的位置即可。那么,假设我们已经知道了需要删除那个位置的节点,我们来继续思考一个问题:删除也就是把绳子剪断,剪下一个节点之后再把修剪好的前后两个部分连接起来。那么我们找到待删除节点的前一个节点,保存它的下一个节点之后再让它的指针指向它的下下个节点,再把保存下来的节点释放掉。这样我们就完成了剪下和重新连接的操作。代码如下:
linknode delete_by_position(linknode head, int pos) {
linknode p = head;
int num = 1;
if (p->next == NULL) {
cout << "this linklist is empty!" << endl;
}
while (p != NULL && num < pos) {
p = p->next;
++num;
}
linknode tem = p->next;
p->next= p->next->next;
delete tem;
return head;
}
4.通过两节内容,我们实现了单链表的创建,遍历输出,插入,删除的基本操作。全部代码如下:
#include<iostream>
using namespace std;
typedef struct linklist {
int data;
struct linklist*next;
linklist(int x) :data(x) {};
}node,*linknode;
/*链表的创建*/
/*这里我们使用尾部插入法,创建一个新的链表*/
linknode creat() {
linknode head = new linklist(0);//头节点初始化为0
linknode p = head;//尾部节点等于头部节点
linknode temnode = NULL;//待插入节点
int c = 1;
int x = 0;
cout << "please enter the data:" << endl;
while (c) {
cin >> x;
if (x) {
temnode = new linklist(x);
p->next = temnode;
p = temnode;
}
else {
c = 0;
}
}
p->next = NULL;
return head;
}
linknode insert_by_position(linknode head, int pos, int data) {
linknode p = head;
linknode newnode = new linklist(data);
int num = 1;
while (p!=NULL&&num<pos)
{
p = p->next;
++num;
}
newnode->next = p->next;//原来的next连接到新的节点
p->next = newnode;//原来的pos位置变成现在的newnode
return head;
}
linknode delete_by_position(linknode head, int pos) {
linknode p = head;
int num = 1;
if (p->next == NULL) {
cout << "this linklist is empty!" << endl;
}
while (p != NULL && num < pos) {
p = p->next;
++num;
}
linknode tem = p->next;
p->next= p->next->next;
delete tem;
return head;
}
void print_linklist(linknode l) {
linknode p = l->next;
if (l->next = NULL) {
cout << "this linklist is empty" << endl;
}
else
{
while (p)
{
cout << p->data << " ";
p = p->next;
}
}
}
int main()
{
linknode p = creat();
p = insert_by_position(p, 3, 5);
p = delete_by_position(p, 3);
print_linklist(p);
return 0;
}
这里需要注意的是主函数:千万注意,print_linklist()函数对于一个相同的链表只能调用一次,因为调用一次之后p指针的指向就到了链表的尾部。再次调用就是一个空的链表。链表实现元素的插入与删除与数组相比时间复杂度只是o(1),数组是o(n)。这是它的优越性。当然劣势也很明显,就是获取某个元素是,它的时间复杂度是o(n),数组是o(1).
5.这只是单向链表,我们将继续讨论更加复杂功能也更加强大的链表以及一些常用的算法。