线性表是一种最基本的数据结构,通常可以分为顺序表和链表。
顺序表即数据的存储空间是连续的,而链表的前后元素需要靠指针来连接。
顺序表简单,但是通常存在着
- 插入删除操作时需要移动大量数据,效率极低;
- 最大表长度难以估计,太大了浪费空间,太小了容易溢出。
但是链表的性质却可以很好的解决以上两个问题。以下是两个链表的示例程序。一个静态,一个动态,均有较详细的注释,以备日后回顾复习。
注意,在Visual Studio的高版本(一般高于VS2013)中运行两个程序时,`scanf()`函数会报错,或者出现这样的问题:
error C4703: 使用了可能未初始化的本地指针变量“xxx”
这时可以打开 项目属性->配置属性->C/C++->SDL检查,选择SDL检查为“否”,即可解决问题。
静态链表示例程序
用数组模拟静态的链表,长度预先定义好,不能动态分配内存。
//简单静态链表
#include <iostream>
using namespace std;
struct student
{
long num;
float score;
struct student * next; //指向该类结构体
}; //注意分号
int main()
{
struct student a,b,c,* head,* p;
a.num = 0001;
a.score = 99;
b.num = 0002;
b.score = 98;
c.num = 0003;
c.score = 97;
head = &a; //头指针
a.next = &b;
b.next = &c;
c.next = NULL; //尾指针
p = head; //迭代的时候不能用头指针本身去迭代,否则就乱了
do
{
cout << p->num << " " << p->score << endl;
p = p->next;
}while( p != NULL );
getchar(); //输出结果后起到暂停作用
}
动态链表示例程序,包含链表常用的若干种操作
用指针实现动态链表,malloc函数和free函数可以实现内存的动态分配。具有较大灵活性。
注意在C++语言中使用printf()
函数要引入标准输入输出库。C语言中使用#include <stdio.h>
,而C++语言中使用#include <cstdio>
来实现。
//动态链表程序
#include "stdafx.h"
#include <cstdio>
#include <iostream>
#include <cstdlib>
using namespace std;
using namespace std;
typedef int ElemType;
typedef struct List * link;
typedef struct List Lnode;
struct List //定义链表
{
ElemType data;
struct List * next;
};
link setnull(link Head) //置空链表,输入一个链表的头指针
{
link p;
p = Head; //头指针赋给指针p
while (p != NULL)
{
p = p->next; //p指向链表的下一个元素
free(Head); //把p指针的上一个指针(Head)所占空间释放
Head = p; //p所指的结点(node)成为新的Head
}
return Head;
}
link insert(link Head, ElemType x, int i) // Head是已知链表,x是要插入的数,i是插入位置
{
link NewPoint, p = Head;
int j = 1;
NewPoint = (link)malloc(sizeof(Lnode)); //生成一个link类型的,长度为一个节点的内存空间
NewPoint->data = x;
if (i == 1) //如果插入位置是第一个,直接令NewPoint为头指针,NewPoint的next指向原第一个结点
{
NewPoint->next = Head;
Head = NewPoint;
}
else
{
while (j<i - 1 && p->next != NULL) //把p指针从头移到要插入的前一位
{
p = p->next;
j++; //此时j应该等于i-1
}
if (j == i - 1) //把NewPoint插进去
{
NewPoint->next = p->next;
p->next = NewPoint;
}
else //超过范围就报错
{
printf("insert is failure,i is not right!");
}
}
return Head; //最后返回更改后的链表指针
}
link create(link Head) //生成链表
{
ElemType newData; //新数据
link NewPoint; //新指针
Head = (link)malloc(sizeof(Lnode)); //给头指针分配一个节点大小的内存空间
printf("please input number: \n");
printf("caution! 倒序输入 \n");
scanf("%d", &newData);
printf("you type %d as the last number\n", newData);
//cin >> newData;
Head->data = newData;
Head->next = NULL; //头指针的后面暂无结点
while (1)
{
NewPoint = (link)malloc(sizeof(Lnode)); //产生新节点
if (NewPoint == NULL) //如果分配内存失败,跳出while
{
break;
}
printf("please input number : input '-1' means exit\n");
scanf("%d", &newData);
//cin >> newData;
if (newData == -1) //结束标志
{
return Head;
}
NewPoint->data = newData; //把NewPoint接在Head前面
NewPoint->next = Head;
Head = NewPoint;
}
return Head;
}
int length(link Head) // 计算一个链表的长度
{
int len = 0;
link p; //不要直接操作Head,会改乱指针
p = Head;
while (p != NULL) //只要p不是空指针,就计数
{
len++;
p = p->next;
}
return len;
}
ElemType get(link Head, int i) // 返回Head链表的i位置的数据
{
int j = 1;
link p;
p = Head;
while (j<i && p != NULL)
{
p = p->next;
j++;
}
if (p != NULL)
{
return (p->data);
}
else//这种情况出现在给定的i超过了链表的长度
{
//printf("data is error!");
cout << "data is error!";
}
return -1;
}
int locate(link Head, ElemType x) //在给定链表中确定数据x的位置
{
int n = 0;
link p;
p = Head;
while (p != NULL && p->data != x) //一直找,找到x前一个停下
{
p = p->next;
n++;
}
if (p == NULL) //如果Head链表里没有这个数据,p会最终指到NULL上
{
return -1;
}
else
{
return n + 1;
}
}
void display(link Head) //在屏幕上打印链表
{
link p;
p = Head;
if (p == NULL)
{
//printf("\nList is empty");
cout << "\nList is empty";
}
else
{
do
{
printf("%d", p->data);
p = p->next;
} while (p != NULL);
}
cout << endl;
}
link connect(link Head1, link Head2) //将两个链表连接起来,Head1在前,Head2在后
{
link p;
p = Head1;
while (p->next != NULL) //p指针一直指到Head1链表的最后一个结点处
{
p = p->next;
}
p->next = Head2; //在Head1最后接上Head2链表的第一个结点
return Head1;
}
link del(link Head, int i) //删除
{
int j = 1;
link p, t;
p = Head;
if (i == 1)
{
p = p->next; //如果要删除的是第一个位置的数据,就把p指针指向下一个元素,然后将Head释放
free(Head); //释放p所指的空间
Head = p; //新的头指针
}
else //如果要删除的元素不在第一个位置
{
while (j < i - 1 && p->next != NULL) //先将p指向要删除的结点的前一个位置,而且要保证要删除的元素没有超出链表的长度
{
p = p->next;
j++;
}
if (p->next != NULL && j == i - 1)
{
t = p->next;
p->next = t->next; //直接跳过第i个结点,将第i-1个结点和第i+1个结点连在一起
}
if (t != NULL)
{
free(t); //把中间变量释放掉
}
}
return Head;
}
int compare(link Head1, link Head2) //比较两个链表是否相同:长度一致,且数据相同
{
link p1, p2;
p1 = Head1;
p2 = Head2;
while (1)
{
if ((p1->next == NULL) && (p2->next == NULL)) //当Head1和Head2同时达到链尾才成立
{
return 1; //两链表相同
}
if (p1->data != p2->data) //比较过程中只要有不一样的数据就返回0
{
return 0;
}
else
{
p1 = p1->next; //不断迭代
p2 = p2->next;
}
}
}
int main()
{
int l;
link head1 = NULL;
link head2 = NULL;
printf("\nNow we generate the first linked list\n");
head1 = create(head1); //生成第一个链表
printf("\nHead1 is :\n"); //显示出来
display(head1);
printf("\nNow we generate the second linked list\n");
head2 = create(head2); //生成第二个链表
printf("\nHead2 is :\n"); //显示出来
display(head2);
printf("\nNow we compare the two linked lists\n"); //比较两个链表是否相同
l = compare(head1, head2);
printf("\nl is %d\n", l);
printf("\nNow we connect two linked lists\n");
connect(head1, head2); //将head1与head2接起来
printf("\nHead1 + Head2 is \n");
display(head1);
printf("\nNow we calculate the first linked list\n");
l = length(head1); //计算head1的长度
printf("\nthe length of link1 is %d\n", l);
l = get(head1, 3); //获取head1链表第三个元素的值
printf("\nthe 3th element of link1 is %d\n", l);
l = locate(head1, 12); //查找12在链表中的位置,没有则返回1
printf("\nlocate 12 is %d \n", l);
printf("\nNow we insert 555 to 8th of the first linked list\n");
head1 = insert(head1, 555, 8); //在第8位插入数据555
display(head1);
printf("\nNow we delete the 7th element of the first linked list\n");
head1 = del(head1, 7); //删除第7个结点
display(head1);
printf("\nNow we clear the first linked list\n");
head1 = setnull(head1); //将head1置空
display(head1);
printf("\nNow wait to exit: press any key and enter\n");
cin >> l;
return 0;
}