一,链表的创建
(1)头插法
Linklist Creat_list(Linklist head) {
head = (Linklist)malloc(sizeof(Lnode)); // 为头指针开辟内存空间
Lnode *node = NULL; // 定义新结点
int count = 0; // 创建结点的个数
head->next = NULL;
node = head->next; // 将最后一个结点的指针域永远保持为NULL
printf("Input the node number: ");
scanf("%d", &count);
for (int i = 0; i < count; i++) {
node = (Linklist)malloc(sizeof(Lnode)); // 为新结点开辟内存空间
node->data = i; // 为新结点的数据域赋值
node->next = head->next; // 将头指针所指向的下一个结点的地址,赋给新创建结点的next
head->next = node; // 将新创建的结点的地址赋给头指针的下一个结点
}
return head;
}
头插法的核心所在是要理解这部分
node->next = head->next; // 将头指针所指向的下一个结点的地址,赋给新创建结点的next
head->next = node; // 将新创建的结点的地址赋给头指针的下一个结点
会发现,头插法创建链表时候,就相当于后来居上。 后面的结点不断往前插,而最后创建的结点在第一个结点处, 第一个创建的结点变成了尾结点
(2)尾插法
void CreatByRear(LinkList head)
{
Node *r,*s;
char name[20];
int number;
r=head;//r指向头结点
printf("请输入姓名和学号:\n");
while(1)
{
scanf("%s",name);
scanf("%d",number);
if(number==0)
{
break;
}
s=(Node *)malloc(sizeof(Node));//分配结点的内存空间
strcpy(s->name,name);
s->number=number;
r->next=s;//原来的结点指向新结点
r=s;//r指向新结点
}
r->next=NULL;//链表的尾结点指针为空
}//CreatByRear函数的功能是创建链表,在该函数中首先定义需要用的指针变量r和s,r指向当前链表的表尾结点,s指向新创建的结点
头插法相对简便,但插入的数据与插入的顺序相反;
尾插法操作相对复杂,但插入的数据与插入顺序相同。
先让p指向,头结点后面那个第一个有效节点。 (红色箭头的p)
然后把头结点的next值修改成,现在第一个有效节点的next成员的值(蓝色的箭头)
然后再释放p,这样不会使链表中途断开,一直删完。当删到最后一个时,head->next变为NULL,销毁正式结束。
void destroy(point *head)
{
point *p;
while (head->next)
{
p = head->next;
head->next = p->next;
free(p);
}
}
有三个参数(point head, int oldrows, int oldcols)
从前往后用一个指针分别指向链表的每一个结构体判断oldrows和oldcols是否相等
我们不管以后的插入删除听起来是想要这个点的位置,其实最重要的是找到他的前驱结点
找到它的前驱结点才能把它"架空",修改掉前驱结点的next值,然后才可以大方的把它的空间释放
需要两个指针:一个负责查找的指针,一个负责记录前驱结点的指针
for (p = head.next; p && ((p->cols != oldcols) || (p->rows != oldrows)); p = p->next)
q = p;
point *found(point head, int oldrows,int oldcols)
{
point *p;
point *q = NULL;
for (p = head.next; p && ((p->cols != oldcols) || (p->rows != oldrows)); p = p->next)
q = p;
return q;
//若指定点为第一个,返回值为NULL
//若指定点为除了第一个以外的其他节点,则该函数的返回值为目标的前一个指定点。
//若指定点不存在,则该函数的返回值指向末节点。
}
思想:定位到它的前驱结点,然后改变它的前驱结点的值,让它指向需要插入的结构体,然后把原始的前驱结点的NEXT赋值给你新插入进来的结构体
q指针是查找返回的值,p指针是新开辟地址,红色是初始指向,蓝色是程序结束的指向。
核心思想代码
if (q == NULL) //新点插入到头结点的后面(原来第一个有效节点前面)
{
p->next = head->next;
head->next = p;
}
else if (q->next==NULL) //指定点不存在,应补充在链表的后面。
{
p->next = q->next;
q->next = p;
}
else
{
p->next = q->next;
q->next = p;
}
//这段3段程序明显都是一个算法,可以进行简化,
if (NULL == pro)
{
q = head;
}
p->next = q->next;
q->next = p;
程序实现
void insert(point *head) //采用左插入节点方式
{
int newrows;
int newcols;
int oldrows;
int oldcols;
point *p;
point *pro;
showtopic("\n当前坐标如下,请参考插入位置进行左插入\n");
showpoints(*head);
showtopic("\n请输入将要插入的位置\n");
scanf("%d %d", &oldrows, &oldcols);
fflush(stdin);
pro = found(*head, oldrows, oldcols);
showtopic("\n请输入你要插入的数据\n");
scanf("%d %d", &newrows, &newcols);
fflush(stdin);
p = create(newrows, newcols);
if (NULL == pro)
{
pro = head;
}
p->next = pro->next;
pro->next = p;
}
步骤
1.让p指向目标节点的前驱结点(如果是第一个点的话,p=head->next)
2.把q->next的值赋值为p所指向实例的next成员,然后p所指向的成员就被“架空”了。
3.释放p的空间。
程序实现
void *delete(point *head)
{
int rows;
int cols;
point *p;
point *pro;
showpoints(*head);
showtopic("\n请输入你所需要删除节点的rows和cols值\n");
scanf("%d %d", &rows, &cols);
fflush(stdin);
pro = found(*head, rows, cols);
if (pro && pro->next == NULL) //这种表达式,如果前面为假后面直接都不用看了,如果你调换了他两个的条件的位置,会引发一个bug。
{
showtopic("\n要删除的节点不存在(按任意键继续....)\n");
getch();
}
else
{
if (NULL == pro)
{
pro = head;
}
p = pro->next;
pro->next = p->next;
free(p);
showpoints(*head);
showtopic("\n删除成功!!!!(按任意键继续)");
getch();
}
return pro;
}
要理解链表的各个结点连接方式,在完成对链表的一系列操作时,要先查找到目标结点的前驱结点,然后通过改变该节点的指向来实现操作。因此要充分理解和掌握链表的查找。链表这一内容,应用到了结构体和指针,要掌握好链表,还需要掌握好指针与结构体的相关知识。