线性表的总结
顺序表还是链表好?
顺序表和链表的逻辑结构都是线性结构,都属于线性表
但二者的存储结构不同,顺序表采用顺序存储,需要预先分配大量的内存空间,但存储效率更高,链表采用链式存储,主要内存有空间就可以分配,操作灵活高效
由于采用不同的存储结构,基本操作的实现效率也不同,顺序表在按位查找元素时,时间复杂度为O(1),但插入删除元素时,平均要移动半个表长的元素,时间复杂度为O(n),采用折半查找的时间复杂度为O(log2n)
链表在按位查找元素时,时间复杂度为O(n),但在插入删除元素时,操作灵活,时间复杂度仅为O(1)
2.3.7习题
2.链式存储能够方便的表示各种逻辑关系
7.数组排序的最好时间为O(nlog2n)
18.在尾结点的单循环链表中,如果删掉最好一个结点,因为没有prior指针,所以需要从头遍历一遍
19.同理,不能忽略删掉最后一个元素,指针的移动问题
25.粗心。看反了顺序
删除不带头结点的单链表为x的结点,要求递归
两种解法,一种我的,一种书上的,在这个题确实书上给的简单,我当时思考的太多了,反而捆住了自己
书:
每次递归只检测第一个结点,是就删掉,不是就L->next进入递归
就是检测以L->next为首结点的链表,下层循环L就是上一层的next
void Deletemin(LinkList& L,int x) {
if (L == NULL)
return;
if (L->data == x) {//第一个结点为所找
LNode* p = L;
L = L->next;
delete p;
Deletemin(L,x);
}
else {
Deletemin(L->next, x);
}
}
我:
分为了两种条件,第一个if,如果第一个结点是所求,就删掉,然后L进入递归
第二个if,就是s指向L的第二个结点,如果是所求,就删掉,然后next进入递归
相当于我一次循环检测了前两个结点,然后书上每次检测一个???
应该是这样,不过确实书上的简单,易于实现思考
void Deletemin(LinkList& L,int x) {
LNode* s = L;
if (L == NULL)
return;
if (L->data == x) {//第一个结点为所找
LNode* p = L;
L = L->next;
delete p;
Deletemin(L,x);
}
if (s->next->data == x) {//不是链表中第一个结点
LNode* m = s->next;//所找结点
s->next = m->next;
delete m;
Deletemin(L->next , x);
}
}
2.带头结点的单链表,删除值为x的结点,并释放空间,不唯一
/*
1.是要找到等于x的结点
2.是要在链表中删除这个结点
3.是要delete这个结点
*/
#include
using namespace std;
typedef struct LNode {
int data;
struct LNode* next;
}LNode,*LinkList;
void DeleteLNode_x(LinkList& L,int x) {
LNode* p = L->next;
LNode* s = L;//p的前驱结点
while (p != NULL) {
if (p->data == x) {
LNode* m = p;
s->next = p->next;
s = p;
p = p->next;
delete m;
}
else {
s = p;
p = p->next;
}
}
}
int main() {
LinkList L;
//没有初始化
int a[10] = { 1,2,3,4,5,6,6,7,6,6 };
DeleteLNode_x(L, 6);
return 0;
}
带头结点,反向输出
一开始我是想用一个数组,正序存放,然后倒序输出
看了答案,提到了递归,先找到链表最后一个结点
if(L->next!=NULL) 递归传入L->next
到了最后一个结点的时候,就进行输出
但还要注意,因为是带头结点的,如果只有递归函数,因为L是指向头结点的,那么在第一层递归的时候就也会进行输出data
所以下面第一层调用时,不选用输出的选项,这样在第一层调用时,不会影响
void R_print(LinkList L) {
if (L->next != NULL)
R_print(L->next);
if (L != NULL)
cout << L->data;
}
void R_printfirst(LinkList L) {
if (L->next != NULL)
R_print(L->next);//是为了不输出头结点
}
原本应该是删除带头结点的最小值的结点,结果写成了找到最小值
自己还觉得写的不错,结果对不上号
原本的一会写,放下面
自己用递归实现的,还以为经过昨天的递归函数,自己有了长进
只要不空,每次就将当前结点的next传入递归,比较min,继续递归
void findmin(LinkList L, int& min) {
if (L != NULL) {//如果第n个不空
if (L->data < min)//小于就改变min
min = L->data;
findmin(L->next, min);//接着传入下一个结点
}
}
void min(LinkList& L) {
int min = L->next->data;//头结点的data
findmin(L->next->next, min);//传入第二个结点
cout << min;
}
删除带头结点的单链表的最小值(唯一)
自己和书上其实有点不一样,怎么觉得我好想比它的好想
书上思想:
用p去遍历链表,pre指向p的前驱,minp保存最小的结点指针,minpre指向*minp结点的前驱,(看到这我就已经蒙了)
其实就是在p和pre顺序遍历的时候,找到min的就将minp和minpre移动过来,嗯,对,就是这样
我就是单纯用p->next去遍历链表,注意!!!不是p去遍历,因为用next去遍历的话,此时p指向的就是min的前驱,可以用另外的结点s保存下来,然后遍历结束,在将p=s->next,进行删除操作,delete即可
void Deletemin(LinkList& L) {
int min = L->next->data;//头结点
LNode* p = L->next;
LNode* s = L;//这里只是初始化
while (p->next != NULL) {
if (p->next->data < min) {
min = p->next->data;
s = p;//s指向每次min的前驱
}
p = p->next;
}
p = s->next;//s是p的前驱,p所指是min
s->next = p->next;//连接结点
delete p;
}
将头结点的单链表倒置,要求辅助空间复杂度为O(n)
我是借助了6题的思想,将链断开产生新链,扫描结点并插入到头结点后面,不过在调试时发现(敲重点)
在进行到链表的最后一个结点时,因为需要一个s是p的后驱,保证不断链,此时s是NULL,会导致程序报错引起读写错误
所以当p是最后一个结点时,即s为NULL时,再另外连接一次
刚才看了答案,思路基本一致,不过他又声明了个结点指向L用于连接,(我觉得差别不大),切,我比他辅助空间还少一个,嘎嘎嘎
void down(LinkList& L) {
LNode* p = L->next;//第一个结点
LNode* s = p->next;//保证不断
p->next = NULL;
p = s;
s = s->next;//s保证不断链
while (p != NULL&&s !=NULL) {
p->next = L->next;//头插
L->next = p;
p = s;
s = s->next;
}
p->next = L->next;//最后一个结点单独插入
L->next = p;
}
把带头结点的单链表的变成递增有序
一开始的想法是,遍历链表,然后保存min值的结点,和第一个结点,遍历完事之后,将min和第一个结点的数值进行交换
然后递归调用
但是!!!在递归调用的时候一直有问题,无奈的我只能在主函数里面用了一个while循环才能正常求解
void Add(LinkList &L){
LNode *p=L->next;//第一个结点
LNode *s=L->next;
LNode *min=s;//最小元素结点标记
while(s!=NULL){
if(s->data<min->data){
min=s;
s=s->next;
}
else
s=s->next;//移动
}
int mm=min->data;//将min移动到第一位
min->data=p->data;
p->data=mm;
//if(L->next!=NULL)
// Add(L->next);
//原本是想这样递归调用的,不过一递归就是有问题
}
int main(){
LinkList L;
List_TailInsert(L);
LNode *p=L;
while(p->next!=NULL){
Add(p);
p=p->next;
}
printList(L);
system("pause");//实验室电脑不加暂停就飞了
return 0;
}
书上的,从前面断开产生新链表,每次扫描一个结点,对在新链表中找到要插入的位置,就是<就向后移动
void add(LinkList& L) {
LNode* p = L->next;
LNode* s = p->next;
p->next = NULL;//断开,产生新链表
p = s;
while (p != NULL) {
s = s->next;//s保证不会断链
LNode* pre = L;//当pre
while (pre->next != NULL && pre->next -> data < p->data)
pre = pre->next;
p->next = pre->next;//找到插入位置,就进行插入
pre->next = p;
p = s;
}
}
删除头结点的单链表中,data在一定范围内的结点
我是一个s,一个s前驱,用s去检测,为所求就用p去跳跃连接,然后delete
不过在第一次运行时,是因为没加else,导致s、p后移量变多
因为如果s是所求,当连接完成后,p还是s的前驱,并不用后移p,对吧,哈哈哈哈
void Delete_between(LinkList& L,int min,int max) {
LNode* p = L;//第一个结点
LNode* s = p->next;
while (s != NULL) {
LNode* r = s;//用于delete
if (s->data > min&& s->data < max) {
p->next = s->next;
s = s->next;
delete r;
}
else {
s = s->next;
p = p->next;
}
}
}
找出两个单链表中的公共结点
就是每次L1中的一个结点,就遍历一下L2,调用了递归
但我还没看答案思路,马上图书馆要关门了,收拾东西溜了溜了
明早来看!!!!!
早上来了,发现自己好像没理解透题目,单链表的公共结点,不是那种简单data相同的结点,是同一块内存的结点,这个是我想的太简单了
单链表的next指针只有一个,所以当有一个公共结点之后,后面所有的结点一定都是相同的,也就是说,如果有公共结点,最后面的必然是公共结点,类似于Y形状。因为他不能中间有一段公共结点,然后又分开了,所以需要两个链表同步的向后扫,如果相等,则后面的都是公共结点(长度较长的需要先找到和断的一齐)齐头并进,对,就是这样,先齐头,再并进
void Findsame(LinkList L1, LinkList L2) {
int m = L1->next->data;
LNode* p = L2->next;
while ( p!= NULL) {
if (p->data == m)
cout << m << " ";
p = p->next;
}
if(L1->next!=NULL)
Findsame(L1->next, L2);
}
LNode *Findsame(LinkList& L1, LinkList& L2) {
int len1 = length(L1);
int len2 = length(L2);
LNode* longlen, *shortlen;
int num = 0;
if (len1 > len2) {
longlen = L1->next;
shortlen = L2->next;
num = len1 - len2;
}
else {
longlen = L2->next;
shortlen = L1->next;
num = len2 - len1;
}
while (num) {//齐头
longlen = longlen->next;
num--;
}
while (longlen != NULL) {//并进
if (longlen==shortlen)
return longlen;
longlen = longlen->next;
shortlen = shortlen->next;
}
return NULL;
}
将一个单链表,按奇偶次序,拆成两个单链表
就直接遍历,单数就插入到第一个结点后面,双数就插入到第二个结点后面,记得定义一个新的头结点,然后链接
LinkList split(LinkList& L) {
LNode *newList = new LNode;
LNode* p = L->next;//奇数
LNode* s = p->next;//偶数
newList->next = s;
LNode* r = s->next;
int j = 3;//计数器
while (r != NULL) {
if (j % 2 == 1) {
p->next = r;
p = r;
r = r->next;
j++;
}
else {
s->next = r;
s = r;
r = r->next;
j++;
}
}
p->next = NULL;
s->next = NULL;
return newList;
}
有一个头结点的单链表{a1,b1,a2,b2…an,bn}
要求拆成{a1,a2…an}和{bn…b2,b1}
定义新头结点,然后遍历,一个尾插,一个头插,over
其中在尾插时,r的后面要有个结点,保证不断链,要不先连r的话,r->next就会丢啦
LinkList split_double(LinkList& L) {
LNode* newList = new LNode;//新的头结点
LNode* p = L->next;//a1
LNode* s = p->next;//b1
newList->next = s;
int j = 3;//判断an还是bn
LNode* r = s->next;//遍历结点
while (r != NULL) {
if (j % 2 == 1) {//an
p->next = r;
p = r;
r = r->next;
j++;
}
else {//bn,这里连接有问题,如果先连r,那就会断链
LNode* k = r->next;//不断链
r->next = newList->next;
newList->next = r;
r = k;
j++;
}
}
p->next = NULL;//断尾
s->next = NULL;
return newList;
}
删除单链表(递增有序)中,数值重复的结点
就是保存前一个结点的data,去比较它后面的结点,相同就删除,不同就更新data为后面的结点数值,继续遍历
注意删除时,p结点的前驱s不用移动
void delete_same(LinkList& L) {
LNode* p = L->next;
LNode* s = L;//p的前驱,用于删除结点
int num = p->data;
p = p->next;
s = s->next;
while (p != NULL) {
if (p->data == num) {
LNode* r = p;//用于delete
s->next = p->next;
//s=s->next;这里不用移动,删除结点,s本身也不动
p = p->next;
delete r;
}
else {
num = p->data;
p = p->next;
s = s->next;
}
}
}