作为几乎是初学C语言的人,在指针的问题还没有完全搞明白的时候,就看到了数据结构里面链表这样一个庞然大物,完全不懂的怎么操作。在阅读了多方资料后,终于根据自己的理解,写出了第一个单向链表程序,放在此处与大家分享,也让自己在复习的时候可以再回顾一下。
程序实现了链表的创建、添加单个元素、删除单个元素的功能;对象为单个int型,即程序中的data。
因为都是用的函数,所以如果要添加其他的操作,只需要写另外的函数就可以了。
下面点明几个个人感觉比较重要的思想。
一、找到前一个元素
想要处理某个元素的时候,要用指针指向该元素(方法是遍历搜索),但同时,如果要执行添加或删除元素的操作,要用另外一个指针找到被处理元素之前的一个元素,这样才能让链表中的指针连接起来,因为链表的核心就是上一个指向下一个,下一个再指向下一个。举个例子,在删除元素的时候,要让被删除元素的上一个元素的指针,指向被删除元素指向的元素,所以在操作上需要找到被删除元素的上一个元素;添加同样是如此,要让上一个元素指向新添加的元素。
在循环创建元素的过程中,用了一个*current指针指向被创建元素的之前一个元素,其作用是解决下面所述的一个问题:
创建头部之后进入循环,创建第一个元素s1后,要把头部的指针指向第一个元素s1,即 head->next = s1;创建第二个元素s2后,要让s1指向s2,即 s1->next = s2;但在循环中,因为s1和s2并不是数组,所以并不能实现这样换元素的操作,自然而然,程序中用了*current来解决这个问题。
进入循环之前创建*current,代表当前创建元素的前一个元素,具体实现方法:
1、头:current = head;(为了之后创建s1后,对head的指针进行操作;因为current也是指针,所以操作current就等于操作head);
2、创建第一个元素s1后:current->next = s1;(刚创建的元素,利用current对head操作,使head指向s1);
current = s1;(因为接下来要创建s2了,为了创建s2后,对s1的指针进行操作(使s1的指针指向s2),所以把s1的值赋给current,等下对current进行操作,就相当于操作s1);
3、创建第二个元素s2后:current->next = s2(此处的current即s1,实现了s1->next = s2);
current = s2;(再为接下来创建新的元素做准备);
···
n、创建最后一个元素sn:current->next = NULL;(最后一个指针指向NULL,链表结束)。
虽然上面写了s1,s2,看起来像是分开的,但在链表中,每一个元素都是*next,所以其实是一样的东西,在循环中并没有s1,s2之分,都是*next。
二、首位插入
本人解决首位插入的问题的方法很简单,可能也比较笨,其实就是把新建立的元素先放在head和第二个元素之间,再交换该元素与头元素的data数据。
1、新创建元素s1
2、s1插入到head后(与在链表中间插入无异,即listInsert函数)
s1->next = head->next;
head->next = s1;
3、交换s1和head的data
s1->data = head->data;
head->data = new data;
即实现把新加入的data放在头尾,head中的data放在第二位。
下面上程序:
#include <stdio.h>
#include <stdlib.h>
struct CELL {
int data;
struct CELL *next;
};
struct CELL * listCreate()
{
printf("创建链表中···\n");
struct CELL *head, *current, *next;
int value;
char flag = 'y';
printf("请输入元素:\n");
scanf("%d", &value);
head = (struct CELL *) malloc( sizeof(struct CELL));
if(head == NULL){
printf("no memory\n");
exit(0);
}
head->data = value;
//current的作用在笔记上
current = head;
printf("是否要继续添加?(y/n)\n");
scanf("%c", &flag);
getchar();
while(flag != 'n' && flag != 'N')
{
printf("请输入元素:\n");
scanf("%d", &value);
getchar();
//每次循环都会给一个新的next的地址
next = (struct CELL *) malloc( sizeof(struct CELL));
if(next == NULL){
printf("no memory\n");
exit(0);
}
next->data = value;
current->next = next;
current = next;
printf("是否要继续添加?(y/n)\n");
scanf("%c", &flag);
}
current->next = NULL;
return head;
}
//head把原函数中的头传进来作寻找用
void listInsert(struct CELL *head)
{
int value, front;
char flag;
printf("新元素是否插入在首位?(y/n)\n");
scanf("%c", &flag);
printf("请输入新元素的值:\n");
scanf("%d", &value);
//首位插入
if(flag == 'y'){
struct CELL *next;
next = (struct CELL *)malloc(sizeof(struct CELL));
//新节点放在head和第二个元素中间
next->next = head->next;
head->next = next;
//交换head和新元素的值,把新的值赋给head,使新值成为头;再把原来的头元素的值赋给新元素,成为第二个位置的值;
next->data = head->data;
head->data = value;
}
else{
printf("请输入要插入位置的前一个元素的值:\n");
scanf("%d", &front);
//新节点
struct CELL *next;
//p为插入位置前一个指针
struct CELL *p;
next = (struct CELL *)malloc(sizeof(struct CELL));
if(next == NULL){
printf("no memory\n");
exit(0);
}
//新节点赋值
next->data = value;
//找到链表的头
p = head;
//用值从头寻找插入元素之前那个元素的值
while(p->data != front)
p = p->next;
//新插入的元素的指针指向下一个元素,即等于前一个元素的指针
next->next = p->next;
//前一个元素的指针指向该新插入的元素
p->next = next;
}
}
void listDelete(struct CELL *head)
{
int value;
//p用来找链表里的值;front指向p前面一个数据,用来连接被删除的前后元素
struct CELL *p, *front;
//找到链表的头
p = front = head;
printf("请输入要删除的元素的值:\n");
scanf("%d", &value);
while(p->data != value){
front = p;
p = p->next;
}
//p前一元素front指针指向原来p指向的下一个元素
front->next = p->next;
free(p);
}
void listViewall(struct CELL *p)
{
while(p->next != NULL)
{
printf("%d\t", p->data);
p = p->next;
}
printf("%d\n", p->data);
}
int main(){
int flag = 0;
struct CELL *p;
//p为头指针,所有的探索都要从头开始
p = listCreate();
printf("以下为所有已添加的数据:\n");
listViewall(p);
while(flag != 4){
printf("请选择功能:\n(1)显示当前所有\n(2)添加一个数据\n(3)删除一个数据\n(4)清除所有数据并退出\n");
scanf("%d", &flag);
getchar();
switch (flag) {
case 1:
listViewall(p);
break;
case 2:
listInsert(p);
listViewall(p);
break;
case 3:
listDelete(p);
listViewall(p);
break;
case 4:
free(p);
printf("程序正在退出···\n");
break;
default:
break;
}
}
return 0;
}
程序没有纠错,希望大家输入的时候谨慎小心。
如果存在bug,或者有什么问题,或者有什么改进方案,亦或是本人犯了什么错误,欢迎大家指出,我会及时回复的。