您的位置:首页 > 理论基础 > 数据结构算法

数据结构--链表的实现

2017-11-10 16:39 316 查看
结构体声明和函数声明

typedef struct Node
{
int data;
struct Node *next;
} link, *link_p;

link_p Create(link_p head);
void Found(link_p head);
link_p Get_node_for_position(link_p head, int position);
void Output(link_p head);
void Insert_node(link_p head, int value, int position);
void Delete_node(link_p head, int position);
link_p Reversal(link_p head);
link_p Merge(link_p h_1, link_p h_2);

链表的创建
创建链表时,首先要创建头结点。头结点的作用是确定一条链表,所以头结点不能移动。在链接每一个创建节点时,都需要一个辅助指针来链接。每创建一个节点,就将辅助指针指向这个节点,然后辅助指针移动到这个节点上,为链接下一个节点做铺垫。

link_p Create(link_p head) //创建链表
{
int i, amount;
link_p temp, new_node;

temp = head; //temp是辅助指针
printf("结点数:");
scanf("%d", &amount);
for (i = 0; i != amount; i++)
{
new_node = (link_p)malloc(sizeof(link)); //创建节点
printf("输入第%d个结点:", i+1);
scanf("%d", &new_node->data); //存入数据
temp->next = new_node; //链接到上一个节点上
temp = new_node; //指针移动到当前已创建的节点上,以便指向下一个节点
}
temp->next = NULL; //收尾
return head;
}

按位置查找

声明一个辅助变量,用来跟踪节点的移动过程。如果该辅助变量和给出的位置相等,就返回该节点的指针,否则返回NULL表示没有找到。还有种查找是按值查找,思路都一样,只是按值查找不需要辅助变量。直接比较给出的值和遍历节点的值就可以。如果和给出的值相等,表示找到。否则链表会一直遍历到链表的末尾结束,这是遍历结束,返回NULL表示找不到

link_p Get_node_for_position(link_p head, int position)//按位置查找结点
{
link_p temp;
int mark = 0;

if (head == NULL || position < 0) //判断链表是否为空和位置的合法性
return NULL;
if (position == 0)
return head;
for (temp = head->next; temp != NULL; temp = temp->next) 
{ //从第一个节点开始查找,每移动一个节点就将mark递增,直到mark的值与position的值相等时,表示找到,然后该节点的指针
++mark; //否则返回NULL,表示找不到
if (position == mark)
return temp;
}
return NULL;
}

插入
插入操作要注意的两点是:

1)要获得的是插入位置上一个结点的指针

2)新结点的指向顺序

第一个问题:如果获得的是插入位置结点的指针,那么新节点的位置将会是插入位置的下一个位置。所以需要获得插入位置的上一个结点的指针,这样才能得到正确位置。

第二个问题:要先将新节点的指针域指向获得的结点的下一个位置,然后获得结点的指针域的指针指向新节点。

void Insert_node(link_p head, int value, int position) //插入结点
{
link_p insert_node, before;

//这里要注意,插入的位置需要获得的是这个位置结点的前一个结点的指针
//这样才能插入到正确的位置。如果获得的是当前位置结点的指针
//那么插入的位置将会出现偏差,向后偏移一个位置
before = Get_node_for_position(head, position - 1); //获取插入位置的前一个结点
if (before)
{
insert_node = (link_p)malloc(sizeof(link)); //创建新节点
insert_node->data = value;
insert_node->next = before->next; //注意正确处理指针的指向
before->next = insert_node;
}
else printf("插入失败\n");
}

删除结点
删除操作还是需要获得要删除结点的上一个结点的指针,并且还需要保证要删除的结点有效,而不仅仅只是保证删除的结点的上一个结点有效。

void Delete_node(link_p head, int position) //删除结点,找到所要删除的前一个结点才能删除该结点
{
link_p delete_node, before;

if (head->next == NULL)
return;

before = Get_node_for_position(head, position - 1); //获取要删除结点的前一个结点
if (before && before->next) //判断要删除前一个结点和要删除的结点都有效
{//这个条件主要是排除要删除的节点是最后一个节点的下一个节点的情况,这样的话获取到的节点刚好是最后一个节点。如果只判断删除节点的前一个节点是否有效,则条件为真
delete_node = before->next; //执行下面的语句,但是这样的话会出现段错误,因为要删除的节点不存在。
before->next = delete_node->next;
printf("已删除结点%d\n", delete_node->data);
free(delete_node);
}
else printf("删除失败\n");
}

倒置

采取前插的方式。先用一个辅助指针确定链表,然后再把当前头结点的下一个结点置位NULL。最后依次取下链表的结点,采用前插的方式,这样就可以让链表倒置过来。

link_p Reversal(link_p head) //链表倒置,从旧链表依次取出结点,插入到头结点的之后,这样链表就反转了
{
link_p temp, p;

temp = head->next; //取链表第一个结点
head->next = NULL; //新链表的头结点
while (temp) //依次插入
{
p = temp;
temp = temp->next;
p->next = head->next;
head->next = p;
}
return head;
}

合并
两条有序链表的合并,前提是这两条链表都是有序递增的。先用两个辅助指针确定两条有序递增链表,然后依次比较两条链表结点的值。小的值就接到新链表上,然后移动。而大的结点值不动,再次比较。指导遍历完毕

link_p Merge(link_p h_1, link_p h_2) //链表合并, 两条递增有序链表合并成一条递增有序链表
{
link_p r, p, q;

p = h_1->next; //分别取出两条链表的首节点
q = h_2->next;

free(h_2); //将其中一个头结点free掉
h_1->next = NULL;
r = h_1; // 用另一个作为新链表的头结点
while (q && p)
{
if (p->data <= q->data){ //将取出的节点相比较,小的节点插入新链表中
r->next = p;
r = p;
p = p->next; //指针移动,另一条不动
}
else{
r->next = q;
r = q;
q = q->next;
}
}
if (p == NULL){
p = q;
}
r->next = p; //当q为NULL的时候,r刚好是q的上一个结点,即r->next = q。所以r指向剩下的p,形成一整条链表
return h_1; //返回已经合并的一条链表
}

全部代码
#include <stdio.h>
#include <stdlib.h>

typedef struct Node
{
int data;
struct Node *next;
} link, *link_p;

link_p Create(link_p head);
void Found(link_p head);
link_p Get_node_for_position(link_p head, int position);
void Output(link_p head);
void Insert_node(link_p head, int value, int position);
void Delete_node(link_p head, int position);
link_p Reversal(link_p head);
link_p Merge(link_p h_1, link_p h_2);

int main(void) //主函数
{
int option, value;
link_p head, head_2;

head_2 = (link_p)malloc(sizeof(link));
head = (link_p)malloc(sizeof(link));
head_2->next = head->next = NULL;
while (1)
{
printf("1.创建链表\n2.按位置查找结点\n3.输出链表\n4.插入结点\n5.删除结点\n6.链表倒置\n7.链表合并\n");
printf("选项:");
scanf("%d", &option);
switch (option)
{
case 1: printf("1.创建第一条\n2.创建第二条\n选择:");
scanf("%d", &option);
if (option == 1)
head = Create(head);
else if (option == 2)
head_2 = Create(head_2);
break;
case 2: Found(head);
break;
case 3: Output(head);
break;
case 4: printf("输入要插入的值和位置:");
scanf("%d %d", &value, &option);
Insert_node(head, value, option);
break;
case 5: printf("要删除的位置:");
scanf("%d", &option);
Delete_node(head, option);
break;
case 6: head = Reversal(head);
break;
case 7: head = Merge(head, head_2);
break;
}
}
return 0;
}

link_p Merge(link_p h_1, link_p h_2) //链表合并, 两条递增有序链表合并成一条递增有序链表
{
link_p r, p, q;

p = h_1->next; //分别取出两条链表的首节点
q = h_2->next;

free(h_2); //将其中一个头结点free掉
h_1->next = NULL;
r = h_1; // 用另一个作为新链表的头结点
while (q && p)
{
if (p->data <= q->data){ //将取出的节点相比较,小的节点插入新链表中
r->next = p;
r = p;
p = p->next; //指针移动,另一条不动
}
else{
r->next = q;
r = q;
q = q->next;
}
}
if (p == NULL){
p = q;
}
r->next = p; //当q为NULL的时候,r刚好是q的上一个结点,即r->next = q。所以r指向剩下的p,形成一整条链表
return h_1; //返回已经合并的一条链表
}

link_p Reversal(link_p head) //链表倒置,从旧链表依次取出结点,插入到头结点的之后,这样链表就反转了
{
link_p temp, p;

temp = head->next; //取链表第一个结点
head->next = NULL; //新链表的头结点
while (temp) //依次插入
{
p = temp;
temp = temp->next;
p->next = head->next;
head->next = p;
}
return head;
}

void Delete_node(link_p head, int position) //删除结点,找到所要删除的前一个结点才能删除该结点
{
link_p delete_node, before;

if (head->next == NULL)
return;

before = Get_node_for_position(head, position - 1); //获取要删除结点的前一个结点
if (before && before->next) //判断要删除前一个结点和要删除的结点都有效
{//这个条件主要是排除要删除的节点是最后一个节点的下一个节点的情况,这样的话获取到的节点刚好是最后一个节点。如果只判断删除节点的前一个节点是否有效,则条件为真
delete_node = before->next; //执行下面的语句,但是这样的话会出现段错误,因为要删除的节点不存在。
before->next = delete_node->next;
printf("已删除结点%d\n", delete_node->data);
free(delete_node);
}
else printf("删除失败\n");
}

void Found(link_p head)
{
int position;

printf("要查找的位置:");
scanf("%d", &position);
link_p node = Get_node_for_position(head, position);
if (node == head){
printf("头结点,没有数据\n");
}
else if (node){
printf("第%d个结点的值为:%d\n", position, node->data);
}
else{
printf("找不到\n");
}
}

link_p Create(link_p head) //创建链表
{
int i, amount;
link_p temp, new_node;

temp = head; //temp是辅助指针
printf("结点数:");
scanf("%d", &amount);
for (i = 0; i != amount; i++)
{
new_node = (link_p)malloc(sizeof(link)); //创建节点
printf("输入第%d个结点:", i+1);
scanf("%d", &new_node->data); //存入数据
temp->next = new_node; //链接到上一个节点上
temp = new_node; //指针移动到当前已创建的节点上,以便指向下一个节点
}
temp->next = NULL; //收尾
return head;
}

link_p Get_node_for_position(link_p head, int position)//按位置查找结点
{
link_p temp;
int mark = 0;

if (head == NULL || position < 0) //判断链表是否为空和位置的合法性
return NULL;
if (position == 0)
return head;
for (temp = head->next; temp != NULL; temp = temp->next) 
{ //从第一个节点开始查找,每移动一个节点就将mark递增,直到mark的值与position的值相等时,表示找到,然后该节点的指针
++mark; //否则返回NULL,表示找不到
if (position == mark)
return temp;
}
return NULL;
}

void Insert_node(link_p head, int value, int position) //插入结点
{
link_p insert_node, before;

//这里要注意,插入的位置需要获得的是这个位置结点的前一个结点的指针
//这样才能插入到正确的位置。如果获得的是当前位置结点的指针
//那么插入的位置将会出现偏差,向后偏移一个位置
before = Get_node_for_position(head, position - 1); //获取插入位置的前一个结点
if (before)
{
insert_node = (link_p)malloc(sizeof(link)); //创建新节点
insert_node->data = value;
insert_node->next = before->next; //注意正确处理指针的指向
before->next = insert_node;
}
else printf("插入失败\n");
}

void Output(link_p head) //输出链表
{
link_p temp = head;

for (temp = head->next; temp != NULL; temp = temp->next){
printf("%d ", temp->data);
}
printf("\n");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: