DS博客作业02--线性表

0.PTA总分

  • 线性表

1.本周学习总结

1.1 总结线性表内容

顺序表

  • 顺序表结构体定义
    顺序表包括元素跟顺序表的长度,元素用数组存放
typedef struct 
{	
    int data[Size];      //存放顺序表元素
    int length ;         //存放顺序表的长度
} List;	
typedef List *SqList;    //指针
  • 顺序表插入
    由于元素仍用数组储存,所有插入数据时移动数组时必不可免的,但仍需注意的时每次插入完,顺序表长度(lenth)需加1
for (i = 0; i < L->length; i++)                     //遍历顺序表
{
    if (L->data[i] >= x)                            //找到要插入的位置
    {
        for (k = L->length; k > i; k--)
        {
            L->data[k] = L->data[k - 1];            //将该位置往后所有元素右移一位
        }
        L->data[i] = x;                             //插入元素
        break;
    }
    else if (x >= L->data[L->length])               //如果在顺序表尾,则直接添加,无需移动
    {
        L->data[L->length] = x;
    }
}
L->length += 1;                                     //注意每次插入顺序表长度加1
  • 顺序表的删除
    在删除时,其本质跟插入差不了多少,只不过一个右移一个左移,注意顺序表长度减1
for (i = 0; i < L->length; i++)                     //遍历顺序表
{
    if (L->data[i] == x)                            //找到要删除的位置
    {
        for (k = i; k <L->length; k++)
        {
            L->data[k] = L->data[k + 1];            //将该位置往后所有元素左移一位,覆盖原位置元素
        }
        L->length -= 1;                             //顺序表长度减1
    }
}

链表

  • 链表结构体定义
    链表结构体包括所储存的元素以及指向下一结点的指针
typedef struct LNode  		        //定义单链表结点类型
{
	ElemType data;                    
	struct LNode *next;		//指向后继结点
} LNode,*LinkList;
  • 链表 头插法
    每次插入的数据均在头结点下一位,这样使得输入跟输出的顺序截然相反。
void CreateListF(LinkList& L, int n)//头插法建链表,L表示带头结点链表,n表示数据元素个数
{
	L = new LNode;
	L->next = NULL;                //开辟头结点

	LinkList head, p, pre;

	head = L;

	int i;

	for (i = 0; i < n; i++)
	{
		p = new LNode;
		cin >> p->data;
		p->next = head->next;
		head->next = p;
	}
}

  • 链表 插入、删除
    链表中插入输出操作相对于数组而言简单不少,无需任何的移动,仅需改变链的关系即可。

    插入

while (pre->next)                    //由于插入时需要知道插入位置的前驱,所有采用pre—>next为循环变量。
{
	if (pre->next->data >= e)
	{
		p->next = pre->next;        //改变链的关系
		pre->next = p;
		break;
	}
	pre = pre->next;
}

删除

while (pre->next)                //删除时需要知道删除位置的前驱,采用pre—>next为循环变量。
{
	if (pre->next->data == e)
	{
	        p = pre->next;        //保留删除节点
	        pre->next = p->next;    //改变链的关系
	        free(p);                //释放所要删除节点
                break;    
	}
	pre = pre->next;
}

有序表

  • 有序单链表数据插入、删除
    其操作的思路及核心代码跟普通链表的插入删除没有区别,只不过需判断特殊情况
    插入
while (pre->next)
{
	if (pre->next->data >= e)
	{
		p->next = pre->next;
		pre->next = p;
		flag = 0;                //标记状态 说明已经找到位置并完成插入
		break;
	}
	pre = pre->next;
}
if (flag == 1)pre->next = p;            //若还没找到插入位置 说明插入位置在链尾,做单独处理

删除

while (pre->next)
{
	if (pre->next->data == e)
	{
		p = pre->next;
		pre->next = p->next;
                free(p);
		flag = 0;                //标记状态 说明找到删除位置
                break;    
	}
	head = head->next;
}
if (flag == 1)cout << e << "找不到!" << endl;  //若没找到 输出相应提示

  • 有序单链表表合并
    同时遍历两链表,并比较数据大小,将较小的采用头插法插入另一条链。
while (L1->next&&L2->next)
{
	if (L1->next->data > L2->next->data)        //当L2数据小于L1时,采用头插法插入
	{
		p = new LNode;
		p->data = L2->next->data;
		p->next = L1->next;
		L1->next = p;
		L2 = L2->next;
	}
        else if(L1->next->data == L2->next->data)L2 = L2->next;        //如果两数据相等则跳过
	L1 = L1->next;
}
if (L1->next == NULL)L1->next = L2->next;            //当L1遍历完,将L1尾部接上L2剩下的数据。

  • 循环单链表
    类似于一个圈,任意一个节点均能访问整条链
//带头结点为L
while(pre->next!=L)
//不带头节点
while(p!=L)
  • 双链表
    即有两个方向的两边,一个节点既有前驱指针,也有后驱指针

定义

typedef struct node
{
    int data;
    struct node* pre;        //指向前一节点的指针
    struct node* next;       //指向后一节点的指针
}DLNode, *DLinkList;
  • 循环双链表
    即在双链表的基础上围成一个圈
    (就不画图了)

1.2 线性表的认识及学习体会。

由于线性表内容中大部分知识点在上学期C的学习中已经接触了不少,所以对于线性表的学习即操作中并没有较大的障碍。刚接触时一些较新的知识点,会出现一脸懵的状态,比如头插法,单链表逆置等,还是花了一定的时间去理解消化并准备活用。总而我觉得线性表比较灵活,需要较好的掌握它。


2.PTA实验作业

2.1 6-2 jmu-ds-有序表插入数据

2.1.1 代码截图

2.1.2本题PTA提交列表说明


1.部分正确:考虑过于简单,只写了插入数据位于两数之间的代码,情况并没有考虑,导致较多错误。随后补上了插入在头和在尾的代码。
2.部分正确:忘记考虑表空情况。加上了判空代码,也就相对完整了。
3.全部正确。


2.2 6-9 jmu-ds-有序链表合并

2.2.1 代码截图

2.2.2本题PTA提交列表说明


1.部分正确:代码思路有问题,我采用每次判断一对数字,插入玩均往下走。导致会出现输入1 1 ,2 2 输出1 2 1 2的情况。后来更改为一旦L2数据小于L1就采用头插法插入L1。
2.部分正确:由于细节问题,L2插入完忘记往下走。随后补上代码。
3.部分正确:没有看清题目具体要求,即:“合并后需要去除重复元素”。加上若两数相等,则L2继续下走。
4.全部正确。


2.3 6-10 jmu-ds-有序链表的插入删除

2.3.1 代码截图

2.3.2本题PTA提交列表说明


1.大部分段错误:为了找到错误具体在哪一部分而提交的。
2.运行超时:同上。
3.段错误:删除函数中,进行一次删除后我仍使指针后移并且在外层仍有一个指针后移代码,这导致一次移动2个位置,出现段错误。后在插入代码中加入break。
4.答案错误:修正段错误代码后忘记添上删除部分代码,直接提交导致错误。
5.部分正确:链表全删时,输出重复。因为链表为空时进不了循环就没注意,然而函数中仍会输出“找不到”。随后加上链表为空直接返回的代码。
6.全部正确。


3.1 1.6判断一个单链表中是否有环

3.1.1 该题的设计思路

3.1.2 该题的伪代码

定义两个快,慢指针fast,slow;
while(fast != NULL && fast->next != NULL)
{
    slow每次后移一位;
    fast每次后移2位;
    if (fast==slow)//说明两者相遇
    return true;
}
end while
return false;
//当fast或fast->next移动到NULL时,或链表为空或只有一个元素时。不符合,返回false。

本题时间复杂度O(n);空间复杂度O(1)

3.1.3 运行结果

先简单的通过手动改代码实现带环

3.1.4分析该题目解题优势及难点。

  • 采用快慢指针操作,觉得比较新颖,所以摘抄此题目为例题。其类似借鉴于数学中的追及相遇问题,速度不同两者绕环跑时必然会相遇。借此编写代码十分巧妙
  • 循环条件不能遗漏'fast->next != NULL',否则可能会出现访问非法内存错误。我觉得这是一个注意点。

3.2 相交链表(LeetCode160题)


3.2.1 该题的设计思路

3.2.2 该题的伪代码

定义两条链L1,L2

while (L1) 
{
        计算L1长度lenthA;
}end while
while (L2) 
{
	计算L2长度lenthB;
}end while
计算长度差size=fabs(lenthA-lenthB);
长的链先走size个节点;
while(L1&&L2)//同时同速遍历两链表
{
    if 两链中节点相同
        return 相同元素节点
}end while
return NULL;

本题时间复杂度O(1)空间复杂度O(n)

3.2.3 运行结果

其中我自建一个链表,分别加在L1,L2的尾部使它们有公共节点。输入的为L1,L2,输出为第一个相同节点的数据。

3.2.4分析该题目解题优势及难点。

  • 先做长度处理,使其类似进行尾部对齐,从较短链开始同时同速遍历,无需嵌套循环。
  • 比较时,注意比较的是节点,而不是数据。这是两个截然不同的东西。
posted @ 2020-03-07 21:40  1911-黄荣煌  阅读(233)  评论(0编辑  收藏  举报