单链表的创建与输出,插入与删除,查询及应用

本文详细介绍了链表作为一种动态存储分配的数据结构,解决了数组在存储和操作上的局限性。链表包括单链表的概念,特点,以及如何通过头指针、节点结构创建、插入、删除和遍历链表。通过实例展示了如何使用链表存储学生信息并进行操作,如头插法和尾插法创建链表,以及查找、插入和删除节点的方法。
摘要由CSDN通过智能技术生成

在信息管理系统的程序设计中,会用到大量的数据记录表格,如果采用结构体数组存储这些数据,会出现一些问题

1.数组必须定义固定的长度要按照可能遇到的最大数目定义数组长度,会造成空间浪费

2.在数组中插入,删除一个元素,需移动数组中的大量元素,尤其是数组长度特别大的时候需要移动端元素更多,这样会耗费大量时间,效率很低

那么,怎样解决这些问题呢?

我们可以采用动态存储分配的数据结构-——链表,

链表的特点是:用则申请,不用则释放,插入和删除知识少量操作,这丫没买过就可以大大地提高空间利用率和时间效率,要实现动态链表,必须在程序运行的过程中能根据需要来分配空间或释放空间

我们引入一个案例

编写一个包含学生信息的链表结构,并将链表中的信息进行输出

链表的结构示意图如下

在链表中,有一个头指针变量即head用这个指针变量保存一个地址,即一个变量的地址,也就是说,头指针指向一个变量,这个变量称为元素。每个元素包含两个部分,数据部分和指针部分,数据部分存放元素所包含的数据,指针部分用来只向下一个元素。最后一个元素的指针指向NULL,表示指向的地址为空

在链表中,第一个结点前虚加一个头节点,头指针指向头结点,头结点的指针域指向第一个实际有效节点,也可称首元结点.头节点的数据域可以不使用,带头结点的链表比不带头结点的链表在创建,插入,删除等操作时代码更加简洁

可以将链表想象成铁链,一环套一环,通过头指针寻找链表中的元素,

在链表中。如果每个结点只有一个指针,所有结点都是单线联系,

除了末尾结点指针为空之外,每个节点的指针都指向下一个结点,一环扣一环形成一条线性链,则称此链表为单向线性链表或单链表

单链表的特点

1. 有一个head指针变量,存放头结点的地址,称为头指针。

2. 头结点的指针域head->next,存放首元结点的地址

3. 每个结点都包含一个数据域和指针域,数据域存放实际数据,指针域存放下一个结点的地址

4. 最后一个结点不再指向其他结点,指针域为NULL,表示链表到此结束指向表尾结点的指针称为尾指针

5. 链表各结点的顺序关系由指针域next确定,链表依靠指针相连不需要真难用一片连续的内存空间

6. 随着处理数据量的增加,链表可以不受程序中变量定义的限延长

单链表的初始化

例如建立一个链表表示一个班级,结点表示学生,结点结构体定义如下:

typedef struct node
{
	char name[20];
	int number;
	struct node *next;
}Node,*LinkList;//Node是结构体类型,LinkList 是结构体指针类型 。
				//LinkList head相当于Node *head,也相当于struct node *head; 

在next成员定义中,引用了本结构体类型,也就是说该类型中采用了递归

单链表初始化代码

linklist initlist()
{
	linklist head;
	head=(Node*)malloc(sizeof(Node));
	head->next=NULL;
	return head;
}

尾插法建立单链表

void creatbyyear(linklist head)
{
	node *r,*s;
	char name[20];
	int number;
	r=head;
	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->next=NULL;
} 

creatbyyear函数的功能是创建链表,在该函数中,首先要定义指针变量r和s,r指向当前单链表的表尾结点,s指向新创建的结点。

在while循环中,读入姓名和学号,如果学号为0,结束输入退出循环,接着用malloc函数分配内存,用s指向新分配的内存,分别赋值姓名和学号;将原来最后一个结点的指针r指向新结点(r->next=s),r也指向s;

创建完一个结点后,循环再次分配内存,输入数据,输入学号为0时退出循环,最后将链表的最后一个结点的指针域赋为NULL,表示链表创建结束。

一个单链表通过动态分配内存方式创建成功

头插法创建单链表

从空表开始重复读入数据,生成新结点,将读入数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头结点之后,直至读入结束标志为止,头插法数据读入顺序和链表的结点顺序正好相反,即data3,data2,data1;

单链表的遍历

如何遍历链表并将其中的数据进行输出呢?

引入一个函数用来输出链表中的数据output,引用一个临时指针t进行循环操作,head为头指针,

voidoutout(linklist head) 
{
	node*t;
	t=head->next;
	while(t!=NULL)
	{
		printf("姓名",t->name);
		printf("学号",t->number);
		t=t->next;
	}
}

将各功能的大门整合到一起,编写一个包含学生信息的链表结构,并且将链表中的信息进行输出

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct node
{
	char name[20];
	int number;
	struct node *next;
}Node,*linklist;
//Node是结构体类型,LinkList 是结构体指针类型 。
				//LinkList head相当于Node *head,也相当于struct node *head; 

linklist initlist()
{
	linklist head;
	head=(Node*)malloc(sizeof(Node));
	head->next=NULL;
	return head;
}
void creatbyrear(linklist head)
{
	node *r,*s;
	char name[20];
	int number;
	r=head;
	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->next=NULL;
} 
void creatbyhead(linklist head)	          	//头插法建立链表 
{
	node *s;
	char name[20];
	int number;
	printf("请输入学生姓名和学号:\n");
	while(1)
	{
		scanf("%d",number);
		if(number==0)
		{
			break;
		}
		s=(node*)malloc(sizeof(node));
		strcpy(s->name,name);
		s->number=number;
		s->next=head->next;
		head->next=s;
	}
}

	
void output(linklist head) 
{
	node*t;
	t=head->next;
	while(t!=NULL)
	{
		printf("姓名:%s\n",t->name);
		printf("学号:%d\n\n",t->number);
		t=t->next;
	}

}
int main()
{
	linklist ha,hb;  //定义单链表头指针 
	ha=initlist();   //初始化单链表
	creatbyrear(ha) ;
	output(ha);
	hb=initlist();
	creatbyhead(hb);
	output(hb);
	return 0;
	
}

在main函数中,定义了头指针ha,然后调用initlist函数初始化单链表,并将链表的头结点返回给ha 指针变量再利用得到的头指针作为creatbyrear的参数,尾插法创建单链表,最后调用output输出ha链表中各结点的信息,hb头指针同理

单链表的插入

链表的插入可以在链表的头指针位置进行插入,也可以在链表某个结点处进行插入但操作的思想相同以在指定位置进行插入为例,

插入结点的过程就像手拉手的小朋友连成一条线,这是来了一个新朋友,他要站在第二个和第三个小朋友之间,那么就需要第二个小朋友放开第三个小朋友的手,拉上新小朋友的手,新小朋友的手蜡烛第三个小朋友的手,重新连成一条线并且新加入的小朋友变成了这条链上的第三个

设计一个函数,在单链表的第i个位置插入新结点

首先从链表的头结点开始,找到链表的第i-1个结点的地址t,若该结点存在,可以在第i-1个结点后面插入第i个结点,为插入的新结点分配内存,然后想新结点输入数据。插入时,首先将新结点的指针s指向原来第i个结点(s->next=t->next),然后将第i-1个结点指向s(t->next=s),这样就完成了插入结点的操作

void insert(linklist head,int i) 
{
	node *t=head,*s;
	int j=0;
	while(j<i-1&&t!=NULL)
	{
		t=t->next;
		j++;
	}
	if(t)
	{
		printf("输入待添加学生的姓名和学号:\n");
		s=(node*)malloc(sizeof(node));
		scanf("%s",s->name);
		scanf("%d",&s->number);
		s->next=t->next;
		t->next=s
		
	}
}

main函数

int main()
{
	linklist ha;  //定义单链表头指针 
	ha=initlist();   //初始化单链表
	creatbyrear(ha) ;
	output(ha);
	insert(ha,3);
	output(ha);
	return 0;
	
}
请输入学生的姓名和学号:
zz 1
xx 2
cc 3
vv 4
bb 0
姓名:zz
学号:1

姓名:xx
学号:2

姓名:cc
学号:3

姓名:vv
学号:4

输入待添加学生的姓名和学号:
nn 3
姓名:zz
学号:1

姓名:xx
学号:2

姓名:nn
学号:3

姓名:cc
学号:3

姓名:vv
学号:4

Press any key to continue

输出结果如上图

单链表的删除

delete函数中有两个参数,head表示链表的头指针,pos表示要删除节点在链表中的位置定义整型变量j来控制循环次数,定义指针变量p表示该结点之前的结点。接着利用循环找到要删除的结点之前的结点p;如果该结点存在并且删除结点存在,则将指针变量q指向待删除结点(q=p->next)

然后(p->next=q->next),free(q);

代码


void Delete(linklist head,int pos) 
{
	node *p=head,*q;
	int j=0;
	printf("删除第%d个学生\n",pos);
	while(j<pos-1&&p)
	{
		p=p->next;
		j++;
	}
	if(p==NULL||p->next==NULL)
	printf("the pos is error");
	else
	{
		q=p->next;
		p->next=q->next;
		free(q);
	}
}
int main()
{
	linklist ha;  //定义单链表头指针 
	ha=initlist();   //初始化单链表
	creatbyrear(ha) ;
	output(ha);
	Delete(ha,3);
	output(ha);
	return 0;
	
}

结果如下

学号:1

姓名:x
学号:2

姓名:c
学号:3

姓名:v
学号:4

删除第3个学生
姓名:z
学号:1

姓名:x
学号:2

姓名:v
学号:4

Press any key to continue

单链表的查询

search函数有两个参数,head表示链表的头指针,name表示要查找的值,定义指针变量p,使其从首元结点开始到链表结束,如果某节点的成员值和给定不等,则继续查找下一个结点p=p->next;

如果查找成功,则返回结点的地址值,若查找失败,则打印提示信息,并返回NULL;

代码如下

node *search(linklist head,char name[])
{
	node*p=head->next;
	while(p)
	{
		if(strcmp(p->name,name)!=0)
		p=p->next;
		else
		break;
	}
	if(p==NULL)
	printf("没有找到值为%s的结点",name);
	return p;
} 
int main()
{
	linklist ha; 
	node *p;//定义单链表头指针 
	ha=initlist();   //初始化单链表
	creatbyrear(ha) ;
	p=search(ha,"李四");
	printf("\n查找到信息如下\n");
	printf("姓名:%s\n",p->name);
	printf("学号:%d\n",p->number);
	return 0;
	
}

结果如下

请输入学生的姓名和学号:
张三 1
李四 2
汪汪 3
麻麻 4
慢慢 0

查找到信息如下
姓名:李四
学号:2
Press any key to continue

### 回答1: Qt是一个功能强大的跨平台应用程序开发框架,提供了丰富的类库和工具,用于快速开发各种类型的应用程序。在Qt中,可以使用Qt的内置类QSqlDatabase和QSqlQuery来进行数据库的连接和查询操作。 首先,需要在Qt项目中添加数据库相关的库文件,并在代码中引入相应的头文件。然后,可以通过以下代码进行数据库的连接: ``` QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); // 连接SQLite数据库 db.setDatabaseName("mydatabase.db"); // 数据库名称 if(db.open()) { qDebug() << "数据库连接成功!"; } else { qDebug() << "数据库连接失败!"; } ``` 上述代码中,我们首先指定数据库类型为SQLite,然后设置数据库名称为mydatabase.db,并尝试打开数据库连接。如果连接成功,就会输出连接成功的信息。 接下来,可以使用QSqlQuery类来执行数据库查询操作,并将结果输出到方框中。假设我们要查询名为"students"的表中的所有记录,并将结果显示在QPlainTextEdit控件中,代码如下: ``` QSqlQuery query; query.exec("SELECT * FROM students"); // 执行查询语句 QString result; while(query.next()) { QString name = query.value(0).toString(); // 获取第一个字段的值(假设为姓名) int age = query.value(1).toInt(); // 获取第二个字段的值(假设为年龄) result += QString("姓名:%1,年龄:%2\n").arg(name).arg(age); } ui->plainTextEdit->setPlainText(result); // 将结果显示在QPlainTextEdit控件中 ``` 上述代码中,我们通过执行"SELECT * FROM students"查询语句,获取到了表中所有记录的姓名和年龄,并将结果拼接成一个字符串,最后使用setPlainText()方法设置到QPlainTextEdit控件中。 通过以上操作,我们就可以实现Qt数据库查询并将结果输出到方框中的功能。当然,具体的实现还可能会有一些差异,根据实际情况做适当的调整。 ### 回答2: 使用Qt编写一个程序来实现数据库查询输出到方框中,可以按照以下步骤进行操作: 1. 首先,需要在Qt项目中添加相关的数据库模块。可以选择使用Qt自带的Sql模块,也可以使用第三方库,例如QtSQLite或QtMySQL。 2. 在程序的主窗口中,创建一个QLineEdit方框用来输入查询语句,另外创建一个QTableView方框用来显示查询结果。 3. 在程序中连接到数据库,并执行查询语句。可以使用QSqlDatabase来连接数据库,并使用QSqlQuery来执行查询语句。例如: ``` QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName("mydatabase.db"); db.open(); QSqlQuery query; query.exec("SELECT * FROM mytable"); ``` 这个示例代码连接到一个名为"mydatabase.db"的SQLite数据库,并执行一个名为"mytable"的表的查询。 4. 将查询结果输出到QTableView方框中。可以通过创建一个QSqlTableModel来存储查询结果,并将其关联到QTableView方框上。例如: ``` QSqlTableModel *model = new QSqlTableModel(); model->setQuery(query); QTableView *tableView = new QTableView(); tableView->setModel(model); ``` 这个示例代码创建了一个QSqlTableModel,将查询结果关联到该模型,并将其设置为一个QTableView的数据源。 5. 最后,将QLineEdit方框和QTableView方框放置在程序的主窗口中,并设置布局。这样,用户就可以在QLineEdit方框中输入查询语句,并在QTableView方框中看到查询结果了。 通过以上步骤,就可以实现一个简单的Qt程序,用于从数据库查询数据并输出到方框中。可以根据具体需求进一步完善程序,例如添加按钮用于执行查询操作,添加功能来修改和删除数据库中的数据。 ### 回答3: QT是一个跨平台的C++应用程序开发框架,它提供了丰富的功能和库来简化开发者的工作。在QT中,可以使用QT数据库模块来进行数据库查询,并将查询结果输出到方框中。 首先,需要在QT项目中引入数据库模块。可以在.pro文件中添加如下内容: ``` QT += sql ``` 接下来,需要连接到数据库。可以使用QT提供的QSqlDatabase类来连接到数据库。可以使用以下代码来连接到MySQL数据库: ``` QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); db.setHostName("localhost"); db.setDatabaseName("database_name"); db.setUserName("username"); db.setPassword("password"); if (db.open()) { // 数据库连接成功 } else { // 数据库连接失败 } ``` 一旦连接到数据库成功,就可以执行查询了。可以使用QSqlQuery类来执行查询,并获取查询结果。例如,可以使用以下代码执行查询: ``` QSqlQuery query; query.exec("SELECT * FROM table_name"); ``` 然后,可以使用QTableView类来显示查询结果。首先,需要创建一个QTableView对象,然后将查询结果设置为其模型。最后,将QTableView对象添加到窗口中。例如,可以使用以下代码显示查询结果: ``` QTableView *tableView = new QTableView; tableView->setModel(query.model()); tableView->show(); ``` 通过以上步骤,可以实现在QT中进行数据库查询,并将查询结果输出到方框中(即QTableView对象)。当然,还可以对方框进行进一步的设置,如设置表格样式、筛选条件等,以满足具体需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值