# 1.本周学习总结（0-4分）#

## 1.1 总结线性表内容##

• 顺序表结构体定义
typedef struct
{
ElemType data[MaxSize];		//存放顺序表元素
int length;			//存放顺序表的长度
} List;
typedef List* SqList;

• 顺序表插入和删除
void InsertSq(SqList& L, int x)//顺序表插入
{
int i, j;
for (i = 0; i < L->length; i++)
{
if (L->data[i] > x)
{
for (j = L->length; j > i; j--)
{
L->data[j] = L->data[j - 1];
}
L->data[i] = x;
L->length++;
break;
}
}
if (i == L->length)
{
L->data[i] = x;
L->length++;
}
}
void DelSameNode(List& L)//顺序表删除相同元素
{
int i, j, k;
for (i = 0; i < L->length; i++)
{
j = i + 1;
for (; j <= L->length; j++)
{
if (L->data[i] == L->data[j])
{
for (k = j; k < L->length - 1; k++)
{
L->data[k] = L->data[k + 1];
}
L->length--;
}

}
}
}

• 链表结构体定义
typedef struct LNode  		//定义单链表结点类型
{
ElemType data;
struct LNode* next;		//指向后继结点

• 链表头插法、尾插法、有序链表插入、删除
void CreateListF(LinkList& L, int n)//头插法建链表
{
int i;
L = new LNode;
L->next = NULL;
oldptr = L;
newptr = L->next;

for (i = 1; i <= n; i++)
{
nowptr = new LNode;
cin >> nowptr->data;
nowptr->next = oldptr->next;
oldptr->next = nowptr;
newptr = nowptr;
}

}
{
int i;
L = new LNode;
L->next = NULL;
tail = L;
for (i = 1; i <= n; i++)
{
node = new LNode;
cin >> node->data;
tail->next = node;
tail = node;
}
tail->next = NULL;
}
{
p = new LNode;
p->data = e;
pre = L->next;

while (pre->next)
{
if (pre->next->data > e)
{
p->next = pre->next;
pre->next = p;
break;
}
else
{
pre = pre->next;
}
}
if (pre->next == NULL)
{
p->next = pre->next;
pre->next = p;
}
}
{
pre = L;
int flag = 1;
if (L->next == NULL)
{
return;
}
while (pre->next)
{
if (pre->next->data == e)
{
ptr = pre->next;
pre->next = pre->next->next;
delete ptr;
flag = 0;
break;
}
else
{
pre = pre->next;
}
}
if (flag==1)
{
cout << e << "找不到！" << endl;
}
}

• 有序表合并
void MergeList(LinkList& L1, LinkList L2)//合并链表
{
LinkList p1, p2, p3 = L1;
p1 = L1->next;
p2 = L2->next;
L1->next = NULL;

while (p1 && p2)
{
if (p1->data < p2->data)
{
p3->next = p1;
p3 = p1;
p1 = p1->next;
}
else if (p1->data > p2->data)
{
p3->next = p2;
p3 = p2;
p2 = p2->next;
}
else
{
p3->next = p1;
p3 = p1;
p1 = p1->next;
p2 = p2->next;
}
}
if (p1 != NULL)
{
p3->next = p1;
}
if (p2 != NULL)
{
p3->next = p2;
}
}

• 循环链表、双链表结构特点
• 循环链表是单链表的变形，循环链表最后一个结点的指针不为NULL，而是指向了表的前端，为简化操作，在循环链表中往往加入表头结点，循环链表的特点是：只要知道表中某一结点的地址，就可搜寻到所有其他结点的地址。
• 双链表有两个特点：一是可以从两个方向搜索某个结点，这使得链表的某些操作（如插入和删除）变得比较简单； 二是无论利用前链还是后链都可以遍历整个双向链表。

## 1.2.谈谈你对线性表的认识及学习体会。##

• 线性表有两种方式:
1.顺序线性表 (也就是用数组实现的,在内存中有顺序排列,通过改变数组大小实现)
2.链表 (不是用顺序实现的,用指针实现,在内存中不连续！)
顺序线性表的缺点：1.插入和删除操作需要移动大量的元素。在顺序表上插入和删除平均要移动一半的元素。2.表的容量难以确定。因为数组的长度要提前声明。3.数组要求占用连续的存储空间，即使存储单元数超过所需要的数目，如果不连续也不能用。而链式存储就没有这种缺点，每一个结点都是程序员自己向计算机申请的，如果不需要的话就会释放掉，将内存返还给系统，而且表的容量是确定的，各个节点的存储地址是不连续的，各个节点通过自己的数据域存储信息，通过指针域来存储下一个结点的信息。但如果一个结点的指针域数据丢失了，那他的下一个结点就找不到了。
单链表的构建有头插和尾插两种方法，无论是哪一种方法都需要从空表的构建开始。头插法就是新插入的每一个结点都在头节点之后，最先插入的节点变为尾节点，而尾插法是每个插入的结点都是插在链表的最后，作为尾节点出现的。这里代码就不给出。课本上都有，在链表构建好之后就可以进行一系列的操作。比如查找删除插入遍历等等。。。
如果希望除了可以确定一个结点的后继外，还可以找到它的前驱，那么我们就可以构建双链表，就是在单链表的每个结点中在设置一个指向其前驱结点的指针域。和单链表类似，它一般也是由头指针唯一确定。
对于单链表，每个结点只存储了向后的指针，到了尾结点就停止了向后的操作。我们将单链表中终端结点的指针端由空指针改为指向头结点，就使整个单链表形成一个环，这种头尾相接的单链表称为单循环链表，简称循环链表。 循环链表和单链表的主要差异在于循环的判断条件上，之前是判断temp->next是否为空，现在是temp->next不等于头结点，.循环链表最后一个结点的 link 指针不 为NULL，而是指向了表的前端。 它只要知道表中某一结点的地址，就可搜寻到所有其他结点的地址。

# 2.PTA实验作业（0-2分）#

## 2.1.题目1：6-10 jmu-ds-有序链表的插入删除##

### 2.1.2本题PTA提交列表说明。###

Q：全部删除出现错误
A：增加了一个链表为空时的附加条件，让其及时返回
Q：增加的条件出现错误
A：把=改成==就对了


## **2.2.题目2：6-11 jmu-ds-链表分割 **##

### 2.2.2本题PTA提交列表说明。###

Q：分割得到的第二条链表输出不完整
A：修改了循环结束的条件，晚一点结束循环
Q：链表长度为偶数时输出正确，为奇数时输出错误
A：为偶数和奇数分别设置不同的条件


## **2.3.题目1：6-8 jmu-ds-链表倒数第m个数 **##

### 2.3.2本题PTA提交列表说明。###

Q：没有考虑无效位置
A：增加了一个条件判断无效位置
Q：出现了段错误，少考虑一个特殊的无效位置
A：把m=0这个特殊的无效位置考虑进去就对了


# 3.阅读代码（0--4分）#

## 3.1 题目及解题代码##

ListNode* insertionSortList(ListNode* head) {
ListNode *next = nullptr;
while (current) {
next = current->next;
while (pre->next != nullptr && pre->next->val < current->val) {
pre = pre->next;
}
current->next = pre->next;
pre->next = current;
current = next;
}
}


### 3.1.1 该题的设计思路###

• 因为有insert操作，所有我们上来就定义三个指针pre、cur、lat。我们首先考虑2应该插在哪？
• 我们发现2比tmp小，所以我们需要将2插入到tmp前。
• 我们主要实现的操作就是
• 接着我们需要将pre和tmp复位，同时我们需要移动cur和lat找到下一个需要被插入元素的位置。

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

### 3.1.2 该题的伪代码###

       判断是否为空
ListNode *dummyHead = new ListNode(0); //来一个虚拟头结点作为有序链表的头结点
ListNode *next = nullptr;
while (current) {
next记录下一个位置
while (pre->next != nullptr && pre->next->val < current->val) {
pre = pre->next; //pre->next 位置就是需要插入元素的位置
}
end while
把比current大的节点接到current后面
将current接到合适的位置
重置指针
current = next; //接着比较下一个
}
end while
}


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

• 难点：由于是插入排序，存在一种可能使得最小的元素处于原链表中间甚至是末尾位置，此时对于新链表而言其头结点就会发生变化。

## 3.2 题目及解题代码##

k 是一个正整数，它的值小于或等于链表的长度。

public ListNode reverseKGroup(ListNode head, int k) {
if (head == null || head.next == null || k <= 1) {
}
ListNode dummy = new ListNode(0);
ListNode pointer = dummy;

while (pointer != null) {
ListNode lastGroup = pointer;

int i = 0;
for (; i < k; ++i) {
pointer = pointer.next;
if (pointer == null) {
break;
}
}

if (i == k) {
ListNode nextGroup = pointer.next;
ListNode reversedList = reverse(lastGroup.next, nextGroup);
pointer = lastGroup.next;
lastGroup.next = reversedList;
pointer.next = nextGroup;
}
}
return dummy.next;
}
private ListNode reverse(ListNode head, ListNode tail) {
}
ListNode prev = null, temp = null;
}

return prev;
}


### 3.2.2 该题的伪代码###

public ListNode reverseKGroup(ListNode head, int k) {
判断是否为空
ListNode dummy = new ListNode(0);
ListNode pointer = dummy;

while (pointer != null) {
记录上一个子链表的尾
int i = 0;
for (; i < k; ++i) {
pointer = pointer.next;
若为空，结束循环
}
end for
如果当前子链表的节点数满足 k, 就进行反转
反之，说明程序到尾了，节点数不够，不用反转
if (i == k) {
记录下一个子链表的头
反转当前子链表，reverse 函数返回反转后子链表的头
lastGroup 是上一个子链表的尾，其 next 指向当前反转子链表的头
但是因为当前链表已经被反转，所以它指向的是反转后的链表的尾
pointer = lastGroup.next;
将上一个链表的尾连向反转后链表的头
lastGroup.next = reversedList;
当前反转后的链表的尾连向下一个子链表的头
pointer.next = nextGroup;
}
end if
}
end while
return dummy.next;
}
private ListNode reverse(ListNode head, ListNode tail) {
判断是否为空
ListNode prev 和temp 赋为空
进行反转
}
end while
return prev;
}


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

• 优势：把一个很长的链表分成很多个小链表，每一份的长度都是 k (最后一份的长度如果小于 k 则不需要反转)，然后对每个小链表进行反转，最后将所有反转后的小链表按之前的顺序拼接在一起。在反转子链表的时候，上一个子链表的尾知道。下一个子链表的头也知道。当前反转的链表的头尾都知道。
• 难点：其实链表的题目并不需要特别强的逻辑推理，它主要强调细节实现，难也是难在细节实现上面 ，虽然大致的方向知道，但是很可能写着写着就会乱。一般赋值的时候需要新建一个临时节点，可以存储即将移到最前端的那个节点，然后按照位置由后到前的顺序为各个变量的next赋值，写完要检查一下，某个下一行作为右值的变量，在上一行它的next是否被改变了？如果被改变了就要警惕，这可能不是正常的赋值顺序。

posted on 2020-03-08 22:27  王威。  阅读(249)  评论(2编辑  收藏  举报