DS博客作业02--线性表

0.PTA得分截图

1.本周学习总结

1.1线性表内容总结

1.1.1.1顺序表结构定义

typedef int ElemType; 
typedef struct 
{	
    ElemType data[MaxSize];//顺序表的元素
    int length ;//顺序表的长度
} List;	
typedef List *SqList;

1.1.1.2建立顺序表

void CreateList(SqList& L, int n)//n为要输入的数据个数
{
	int i;
	L = new List;//分配内存
	L->length = n;
	for (i = 0; i < n; i++)
	{
		cin >> L->data[i];
	}
}
  • n也可以放在函数内

1.1.1.3顺序表插入数据

void InsertSq(SqList& L, int x)
{
    遍历顺序表L;
    找到使x有序的位置;
    移动数组并插入x;
}
略去一些细节的代码:
for (i = L->length - 1; i >= 0; i--)//从后往前,每一个数把值赋给下一位
{
	if (x > L->data[i])
	{
		L->data[j] = x;
		return;
	}
	L->data[j--] = L->data[i];
}
L->data[0] = x;

1.1.1.4顺序表删除数据

void DelNode(SqList& L, int min, int max)//区间删除数据
{
	遍历顺序表L;
	找到属于[min, max]的元素并删除;
}
省略细节的代码:
for (i = 0; i < L->length; i++)
{
	if (L->data[i] >= min && L->data[i] <= max)
	{
		for (k = i; k < L->length; k++)
		{
			L->data[k] = L->data[k + 1];
		}
		L->length--;
		i--;
	}
}

1.1.1.5销毁顺序表

void DestroyList(SqList &L)
{
  delete L;
}

1.1.2.1链表结构定义

typedef int ElemType;
typedef struct LNode
{
	ElemType data;
	struct LNode *next;
} LNode,*LinkList;

1.1.2.2建新链表

//带头结点
LinkList L;
L = new LNode;
L->next = NULL;

1.1.2.3链表数据插入

head = end = L;
node = new LNode;//循环内,每次结点申请内存
//头插法
node->next = head->next;
head->next = node;

//尾插法
node->next = NULL;//或node->next = end->next;
end->next = node;
end = node;

1.1.2.4链表插入、删除数据

//插入数据,多个数据可在main内循环输入
void ListInsert(LinkList& L, ElemType e)
{
        定义指针p = L;//用于遍历链表
	令node->data = e;
        遍历链表L,找到插入位置;
        用头插法代码插入;
}
//删除数据
void ListDelete(LinkList& L, ElemType e)
{
	定义指针p = L, temp;//temp用于临时存储要删除的结点
        找到与e相等的结点p->next;
        temp存储p->next;
        p->next = p->next->next;//保持链表不中断
        删除temp;
}

1.1.2.5链表的合并

void MergeList(LinkList& L1, LinkList L2)
{
        新建链表L3;
	遍历链表L1,L2;
        比较L1,L2中每个元素大小,每次将小的元素放入L3中;
        结束循环后,若有链表未达到末尾,则插入L3尾部;
        //此时根据题目需求,如果要求和L1或L2公用结点,则加入一句L1 = L3即可
        //若无,可在函数的变量中加入L3
}

1.1.2.6循环链表、双链表结构特点

循环链表特点:

  • 尾结点指向头結点,形成一个环,判断空链和链表结束的代码分别为:head == head->next, rear->next == head

双链表特点:

  • 与单链表的区别每个结点多了一个直接前驱,可以指向前一个结点
  • 循环链表、双链表的优点:无论指针在链表哪个位置都能遍历整个链表

1.2我对线性表的认识和学习体会

一开始学链表的时候,我经常出现断链,或者链接错误,直到我认识到next里存的是下一个结点的地址,才转过弯来,之前一直把next当作箭头使用,导致插入结点的代码顺序都出错,之后通过PTA的训练,熟练了不少。学习顺序表时,因为它和数组异曲同工,并没有那么困难。循环链表和双链表因为缺少题目,可能还不熟练。

2.PTA实验作业

2.1顺序表删除重复元素

2.1.1代码截图

2.1.2PTA提交列表说明

2.1.3遇到的问题

  • 1.部分正确:空链表错误,因为题目没有要求,而我在空链表时输出NULL,操作改为return通过
  • 2.部分正确:删除的位置错误,将前面的重复元素删除,而留下后面的元素,导致输出顺序与题目要求不一致

2.2链表倒数第m个数

2.2.1代码截图

2.2.2PTA提交列表说明

2.2.3遇到的问题

  • 1.部分正确:最开始打算用递归来做,但因为对返回数据的处理的代码写的过于复杂,只能得出部分答案,剩余的不知如何下手
  • 2.部分正确:改用快慢指针的做法,但没有对m=0的特殊情况进行处理,在开头加入判断后通过

2.3链表分割

2.3.1代码截图

2.3.2PTA提交列表说明

2.3.3遇到的问题

  • 1.答案错误:把结点插入L时没处理好,导致两条链交叉,输出的数错乱
  • 2.编译错误:链表越界,系统报错

3.阅读代码

3.1单链表反转(递归)

  • 空间复杂度和时间复杂度都为O(n)

3.1.1设计思路

  • 第一步,遇到空指针,返回head,reverse指向最后一个数
  • 第二步,倒置:①:head->next->next=head(使下一个结点指向自己),②:head->next=nullptr(使自己指向空指针)
  • 一直到回到开头

3.1.2伪代码

//代码为递归算法
ListNode* reverseList(ListNode* head)
{
    if(传入的结点head为空或head->next为空)返回该结点;
    建指针reverse储存最后一个结点的位置;
    使下一个结点指向自己;
    使自己指向空指针;
}

3.1.3运行结果


3.1.4该题目解题优势及难点

  • 优点:代码简洁,时间复杂度小
  • 缺点:比起头插法空间复杂度大,而且不能使用带头结点的链表进入函数,同时逆转后的链表没有头结点
  • 难点:写代码时比较容易找不到思路,或者使用了多层循环导致时间复杂度过大

3.2寻找单链表的中间元素

  • 时间复杂度为O(n),空间复杂度O(1)

3.2.1设计思路

  • 定义快慢指针,快指针一次走俩,满指针一次走一,当快指针走到底时,慢指针正好在中间(奇数偶数都一样)

3.2.2伪代码

public Node getMid(Node head)
{
    if(链表为空)空链表返回;
    定义快慢指针,使其初始位置都在头结点;
    while(快指针还没到底)
    {
        满指针走一步,快指针走两步;
    }
    出循环时慢指针刚好为中间位置,返回满指针;
}

3.2.3运行结果

3.2.4该题目解题优势及难点

  • 优点:只需要遍历一次链表就能完成
  • 难点:这道题其实不难,也可以通过遍历一遍链表得到长度len,再次遍历得到答案。所以这题考察的是如何简化时间复杂度
posted @ 2020-03-08 21:50  林盛泓  阅读(227)  评论(0编辑  收藏  举报