几种线性表(C/C++)(同步头歌线性表实训)

几种线性表(C/C++)

基础知识

线性表基础

C/C++ 结构struct

在C/C++语言中,我们可以自定义一个类型,所使用的函数就是struct
如,统计学生的信息,比如有姓名,学号,排名等,我们就可以这样定义一个类型,然后对这个类型进行直接操作
其基本形式为:

struct name
{
	int a;
	char b;
	int*p;
};

注意最后花括号也有个分号进行隔断。
当然,struct内部也可以用加上struct。


如何读取和使用struct定义类型内部的数据呢?
有两个方法:
1、"."法。
此方法就是用"."来引出定义类型内部的,某个类型值。
例子:

	struct name
{
	int a;
	char b;
	int*p;
};//定义结构name

int num = 0;//声明和初始化;
num = name.a;//取出name结构的int型 a的值;

2、"->"法
此方法需要用到指针,此指针需要是所定义结构类型的指针,然后进行取值。
例子:

struct name
{
	int a;
	char b;
	int*p;
};

name*p;
int num = 0;//初始化
num = p->a;//赋值num 为 name结构的a的值;

C/C++指针

C语言的指针作用强大,涉及对内存地址的处理和使用,若加上动态内存,可以很好的迭代某个过程,如此处的线性表。

线性表

在这里插入图片描述

线性表则是很多个相同结构链接起来,一般可以是,一个线性表的节点内部有一个用于储存数据的变量,一个是指针,指向下一个节点,因此我们可以这样写:

struct node
{
int data;
node*next;
}//定义节点含有的数据

node node1;
node*p =&node1;
node node2;

cin>>p->data;
p->next = &node2;//赋值节点一内部的

很快我们可以发现,这样的话我们扩展时十分麻烦;
我们使用动态内存的方式进行迭代。

动态内存

对于C++中, 我们可以用 newdelete 来实现动态。
exa:

node *t = new node;
t->data = 2;
delete t;//每次用完则在分配的内存中删去t;应用于迭代,则可以实现一次一次的将内存动态分配。

代码

线性表虽然的节点是结构相似的,但是其扩展和添加方法是不一样的。主要是以下三种:

1.顺序线性表

线性表前面的顺序逆序排序均是由数据信息得来。
顺序线性表的意思就是按照输入数据的顺序进行线性表建立。
由于开头的指针指向第一个节点,因此我们需要在最后一个节点进行处理,对其内部储存的next指向下一个节点即可。
具体实现可以是:
1、获得头地址指向第一个节点;
2、得到第一个节点内部的next,并指向下一个节点。
3、循环

但是这样下去,我们会发现很麻烦,麻烦在重复确定最后一个节点,再在这个节点后面加新的节点。
于是我们可以每次从头地址,先用头地址定位到第一个节点,然后就用循环。
如何做到呢?

#include "linearList.h"
node *insertTail(node *h, node *t)
{
    if(h==NULL)
    {
        h = t;
        return t;
    }
    node *p =h;
    while(p->next)
    {
        p = p->next;
    }
    t->next = NULL;
    p->next = t;
    return h;
}

每次都历遍到尾部,从尾部添加新的节点,成为顺序插入。

注:此头文件为:

#include <iostream>
using namespace std;

// 定义结点结构
struct node
{
    int data;  // 数据域
    node * next;  //指针域,指向下一个结点
};

// 函数insertTail:链表尾部插入
// 参数:h-链表头指针,t-指向要插入的结点
// 返回值:插入结点后链表的首结点地址
node *insertTail(node *h, node *t);

// 函数printList:输出链表,每个数据之间用一个空格隔开
// 参数:h-链表头指针
void printList(node *h);

// 函数delList:删除链表,释放空间
// 参数:h-链表头指针
void delList(node *h);

测试文件为:

#include "linearList.h"

int main()
{
	int n,i;
	node *t;
	node *head=NULL; // 头指针为NULL,表示线性表为空,结点数为0
	// 输入结点数
	cin>>n;
	for(i=0;i<n;i++)
	{
		// 为新节点动态分配空间
		t = new node;
		cin>>t->data;  // 输入结点数据
		t->next=NULL;  // 结点指针域值为空
		// 调用函数插入结点
		head = insertTail(head, t);
	}
	// 输出链表
	printList(head);
	// 删除结点,释放空间
	delList(head);

	return 0;
}
// 函数delList:删除链表,释放空间
// 参数:h-链表头指针
void delList(node *h)
{
	node *p=h;  // 指针p指向头结点,第一个要删除的结点
	while(p)  // 这个结点是存在的
	{
		h = h->next;  // 头指针h指向下一个结点(下一个结点的地址存在当前结点的指针域中,即h->next中
		delete p;  // 删除p指向的结点
		p = h;  // p指向当前的头结点,即下一个要删除的结点
	}
}
// 函数printList:输出链表,每个数据之间用一个空格隔开
// 参数:h-链表头指针
void printList(node *h)
{
	cout<<"List:"; 
	while(h)
	{// h为真,即h指向的结点存在,则输出该结点的数据
		cout<<" "<<h->data;  // 输出结点数据
		h=h->next;  // 将该结点的指针域赋值给h,h就指向了下一个结点
	}
	cout<<endl; // 输出换行符
}


2.逆序线性表

逆序线性表听起来貌似比顺序更加麻烦,想清楚后并不困难。
逆序只需要每次从head插入节点即可,注意的是,需要逆序的地方是需要将第一个输入节点(即最后一位输出的节点)的next赋值成为head地址,即他的下一个节点为head。并返回此节点到head,然后再添加的节点(倒数第二个),
其代码实现:

#include "linearList.h"

node * insertHead(node *h, node *t)
{
    t->next = h;
    return t;
}

测试文件

#include "linearList.h"

int main()
{
	int n,i;
	node *t;
	node *head=NULL; //头指针为NULL,表示线性表为空,结点数为0
	//输入结点数
	cin>>n;
	for(i=0;i<n;i++)
	{
		//为新节点动态分配空间
		t = new node;
		cin>>t->data; //输入结点数据
		t->next=NULL;  //结点指针域值为空
		//调用函数插入结点到链表头部
		head = insertHead(head, t);
	}
	//输出链表
	printList(head);
	//删除结点,释放空间
	delList(head);

	return 0;
}
//函数delList:删除链表,释放空间
//参数:h-链表头指针
void delList(node *h)
{
	node *p=h; //指针p指向头结点,第一个要删除的结点
	while(p) //这个结点是存在的
	{
		h = h->next; //头指针h指向下一个结点(下一个结点的地址存在当前结点的指针域中,即h->next中
		delete p; //删除p指向的结点
		p = h; //p指向当前的头结点,即下一个要删除的结点
	}
}
//函数printList:输出链表,每个数据之间用一个空格隔开
//参数:h-链表头指针
void printList(node *h)
{
	cout<<"List:"; 
	while(h)
	{//h为真,即h指向的结点存在,则输出该结点的数据
		cout<<" "<<h->data;  //输出结点数据
		h=h->next;  //将该结点的指针域赋值给h,h就指向了下一个结点
	}
	cout<<endl; //输出换行符
}
}

头文件同上顺序线性表
输出的第一个数是的最后一个输入节点,而每次输入的节点的next应指向前一个节点,因此将每个节点当head返回,下一个节点next指向head(即下一个节点),到最后,最后输入节点就当成head返回,printlist函数则从head开始输出每个数值,而最后输入的节点指向倒数第二个输入的节点,完成逆序线性表。

3.排序线性表

排序表可以算是最麻烦的,需要进行数值的比较再进行指针之间的指向。
参考头歌
利用两个指针p q,第一个指针p从head开始,第二个指针q从输入的节点开始,先进行比较,参考数值内的比较排序法,进行next的交换。
此处有三个指针,p,q,t,其中,p指针是用来确定插入节点的头,q指针用来确定插入指针的尾,t为待插入的指针。
首先来一个节点直接接入head,让q指针有值。此处相当于数值排序中max最大值的初始值(例如数组a[100]初始化的max = a[0])。
此处的思想是,判断排序法。利用每个q作为判断值,大于则插入并继续,若小于,则将p值换成q,q值向后寻,以找到大于新插入的数据大小。
判断方式:
三种情况:
第一种:开头,p指针之前没有节点了,只需处理从head处理,判断。
第二种:结尾,则是q运行到末尾,此处判断p。
第三种:在中间,则是利用p、q指针判断前后,前面已存在的节点指向新节点,新节点指向后已存在的节点,形成插入新的节点。可以想象在两根断绳中间利用打结加一根新的,则需首尾两个结。

再需注意的是,寻找新节点位置的方式。由于q指针比p指针先行动,利用q指针进行循环,中间插入判断插入的条件,再是到达尾部的情况和开头的情况即可。

排序线性表

#include "linearList.h"

node * insertSort(node *h, node *t)
{
    if(h==NULL)
    {
        h =t;
        return t;
    }
    node*p=NULL;
    node*q = h;
    while(q)
    {
        if(q->data > t->data)
        {
            if(p==NULL)//开头head
            {
                t->next = q;
                return t;
            }
            else//在中间位置
            {
                p->next = t;
                t->next =q;
                return h;
            }

        }
        else//如果if的判断条件不成立,则前推p和q,再次进行比较。
        {
            p =q;
            q = q->next;
        }
    }
    if(q==NULL)//到结尾,现插入的数据一定是最大值(由上述的循环可以得到),此时的p值是最后一个节点,则在p的节点连接新节点即可。
    {
        p->next=t;
        t->next = NULL;
        return h;
    }//记得每个判断完成,即是找到相应情况进行返回。
}

注意这里的return,此处的循环时为了判断新输入的节点应在的线性表位置,而不是利用线性表一直往后推。每个新节点找到自己的对应位置以后即是插入成功了。此时返回头指针,以再次进行判断和插入数据。
因此对应的每种情况找到后进行插入,再即是返回h,再输入新的数据,利用动态地址创新的节点再进行下一次的插入判断和返回了。

测试文件为:

#include "linearList.h"

int main()
{
	int n,i;
	node *t;
	node *head=NULL; //头指针为NULL,表示线性表为空,结点数为0
	//输入结点数
	cin>>n;
	for(i=0;i<n;i++)
	{
		//为新节点动态分配空间
		t = new node;
		cin>>t->data; //输入结点数据
		t->next=NULL;  //结点指针域值为空
		//调用函数插入结点,按data从小到大排序插入
		head = insertSort(head, t);
	}
	//输出链表
	printList(head);
	//删除结点,释放空间
	delList(head);

	return 0;
}
//函数delList:删除链表,释放空间
//参数:h-链表头指针
void delList(node *h)
{
	node *p=h; //指针p指向头结点,第一个要删除的结点
	while(p) //这个结点是存在的
	{
		h = h->next; //头指针h指向下一个结点(下一个结点的地址存在当前结点的指针域中,即h->next中
		delete p; //删除p指向的结点
		p = h; //p指向当前的头结点,即下一个要删除的结点
	}
}
//函数printList:输出链表,每个数据之间用一个空格隔开
//参数:h-链表头指针
void printList(node *h)
{
	cout<<"List:"; 
	while(h)
	{//h为真,即h指向的结点存在,则输出该结点的数据
		cout<<" "<<h->data;  //输出结点数据
		h=h->next;  //将该结点的指针域赋值给h,h就指向了下一个结点
	}
	cout<<endl; //输出换行符
}
}
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
栈是一种具有特定操作的线性表,它遵循"先进后出"的原则。头歌c 线性表的应用栈有以下几个方面。 首先,栈在函数调用和递归算法实现中起到重要作用。当一个函数被调用时,函数的局部变量和其他相关信息都被保存在栈中。随着函数的执行过程,栈会不断压入新的数据帧,当函数执行结束后再逐个出栈,恢复之前的执行状态。这种方式使得程序的执行具有一定的顺序性和层次性,方便理解和调试。 其次,栈可以用于表达式求值和后缀表达式的转换。在进行表达式求值时,通过栈的压入和弹出操作,可以按照运算符的优先级进行计算。同时,后缀表达式通过将运算符放在操作数后面的方式,也可以通过栈的弹出和压入操作轻松实现。 此外,栈还广泛应用于编程语言解析器中,用于处理代码的括号匹配、语法分析等任务。当编写一个编译器或解释器时,栈可以用来存储解析过程中的临时数据,判断代码语法的正确性。 最后,栈还可用于实现浏览器的"后退"和"前进"功能。当用户在浏览器中点击后退按钮时,浏览器通过栈结构将访问过的网页地址依次弹出,实现回退的功能。当用户点击前进按钮时,浏览器将之前弹出的地址再次压入栈中,实现前进的功能。 综上所述,栈作为一种特殊的线性表,具有一些独特的应用场景。它在函数调用、表达式求值、语法分析和浏览器导航等领域有重要应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值