学习数据结构的同学肯定都听说过单链表这个东西,原理虽然简单,但是对于初学者来说也是有一定的理解难度的,但是只要勤加练习,相信同学们都能掌握这一基本算法的。
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
下面我将以实现一个电话簿为例子介绍一下单链表的实现方式和应用:
首先就是要列出电话簿的基本内容。
也就是姓名、电话号码、性别以及地址或备注:
struct PHONE {
int ID;//编号
char name[20];
char tellphone[20];
char sex[5];//考虑到大部分人不使用这个选项,可以删去,F女M男
char address[100];//也可以删去
};
接下来就是单链表的基本框架和电话簿类的声明了。这里我使用了模板类来简化代码。
结构体NODE中一共有两项数据,分别是具体数据data存储电话簿信息和next指针实现单链表的链接。
template <class A>
struct NODE {
A data;
NODE<A>* next;
};
template <class A>
class LIST {
public:
LIST();
LIST(A a[], int n);//建立
void add(A temp);//增加
void crash(int n);//删除第n位联系人
void edit();//进入编辑模式
void search(char people[]);//按照人名查找,需要全称
void print();//显示列表
void out();
private:
NODE<A>* first;
int length;
};
下面这两段代码就是电话簿的初始化,也就是单链表的实现:
第一段代码是没有电话簿初始信息的初始化方法,只需要将单链表开端的first指针指向空(NULL)即可。
template <class A>
LIST<A>::LIST() {
first = new NODE<A>;
first->next = NULL;
length = 0;
}
第二段代码则是可以引入电话簿信息的初始化方法,也是单链表的尾部添加实现:
注意先让s接受first的next。
LIST<A>::LIST(A a[], int n)
{
first = new NODE<A>;
first->next = NULL;
NODE<A>* r = first;
for (int i = 0; i < n; i++)
{
NODE<A>* s = new NODE<A>;
s->data = a[i];
s->next = NULL;
r->next = s;
r = s;
}
length = n;
}
增加函数与第二段初始化代码的做法相同
template <class A>
void LIST<A>::add(A temp)
{
NODE<A>* a = new NODE<A>;
a->next = first->next;
a->data = temp;
first->next = a;
length++;
}
删除函数
也是注意先让s接受first的next,以免丢失。
template <class A>
void LIST<A>::crash(int n)
{
NODE<A>* p = first;
int i = 0;
while (p != NULL && (i != n - 1))
{
p = p->next;
i++;
}
NODE<A>* s = p->next;
p->next = s->next;
delete s;
length--;
}
下面是电话簿信息的输出和保存
其中out函数会将信息保存在一个外部txt文件中,在下次打开的时候重新读入以进行操作,将通讯录人数和信息分开放置为了简化代码量。
template <class A>
void LIST<A>::print()
{
NODE<A>* p = first->next;
int i = 1;
while (p != NULL)
{
p->data.ID = i;
cout << p->data.ID <<" 姓名:"<<p->data.name<<" 电话:"<<p->data.tellphone<<" 性别"<<p->data.sex<< endl;
cout << "备注:" << p -> data.address << endl;
i++;
p = p->next;
}
}
template <class A>
void LIST<A>::out()
{
ofstream fs("Data.txt",ios::out);
NODE<A>* p = first->next;
int i = 1;
while (p != NULL)
{
p->data.ID = i+N;
fs<< p->data.ID <<" "<< p->data.name << " " << p->data.tellphone << " " << p->data.sex << " " << p->data.address<< endl;
i++;
p = p->next;
}
fs.close();
ofstream ab("NUM.txt", ios::out);
ab << N + i-1 << endl;
ab.close();
}
只有增加和删除两个基本功能显然是有一些单调的,所以我加入了查询和编辑功能,来实现一些简单的人机交互。
search函数一直定位到可以匹配的人名时停止,其中strstr函数是字符串的模糊比较,也就是说只输入名字的部分就可以查到电话号码,但是这样存在一个缺点,就是无法把重名的人都找出来,这里便留给读者去自由发挥。
edit函数即编辑功能,通常情况下我们修改通讯录不是全部修改,所以这里便增加了选择,让用户可以只修改部分信息。
template <class A>
void LIST<A>::search(char people[])
{
NODE<A>* p = first->next;
while (p != NULL)
{
if ((p != NULL) && (strstr(people, p->data.name) != NULL))
{
cout <<"电话:"<< p -> data.tellphone << endl;
return;
}
else p = p->next;
}
}
template <class A>
void LIST<A>::edit()
{
int no;
cout << "请输入编号" << endl;
cin >> no;
int i=0;
NODE<A>* p = first;
while (p != NULL && (i != no - 1))
{
p = p->next;
i++;
}
cout << " 姓名n 电话t 性别s 备注a" << endl;
char func=NULL;
while (func != 'o')
{
cout << "请输入字母" << endl;
cin >> func;
switch (func)
{
case 'n':
cin >> p->data.name;
break;
case 't':
cin >> p->data.tellphone;
break;
case 's':
cin >> p->data.sex;
break;
case 'a':
cin >> p->data.address;
break;
case 'o' :
cout << "退出编辑" << endl;
break;
}
}
}
最后是主函数的文件读写部分,通讯录的实现就完成了。
ifstream bc("NUM.txt", ios::in);
bc >> N;
bc.close();
char desktop=NULL;
PHONE DATA[1000];
ifstream cd("Data.txt", ios::in);
for (int i = 0; i < N; i++)
{
cd >> DATA[i].ID >> DATA[i].name >> DATA[i].tellphone >> DATA[i].sex >> DATA[i].address;
}
cd.close();
单链表实现电话簿
本文详细介绍单链表数据结构的原理与应用,通过构建电话簿系统实例,讲解单链表的创建、增删改查等核心操作,适用于初学者理解和实践。
5290

被折叠的 条评论
为什么被折叠?



