题目
已知顺序表,请设计算法,将所有偶数放到奇数的左边
思路
(类似于快速排序算法划分的思想)
设置两个指针i,j
1、i从第一个元素开始依次往后遍历,找到值为奇数的的元素停止
2、j从最后一个元素开始依次往前遍历,找到值为偶数的元素停止
3、交换i,j的元素值
void exchange(SqList &L)
{
int i = 0; // i从第一个元素开始依次往后遍历
int j = L.length - 1; // j从最后一个元素开始依次往前遍历
while (i < j)
{
while ((L.data[i] % 2 == 0) && (i < j)) i++; // i找到值为奇数的的元素停止
while ((L.data[j] % 2 == 1) && (i < j)) j--; // j找到值为偶数的元素停止
int temp = L.data[i]; // i、j下标所对应的元素值互换
L.data[i] = L.data[j];
L.data[j] = temp;
}
}
题目
已知一个顺序表L,请设计一个算法,删除其最小元素(最小元素不唯一)
思路
设置两个指针i、j
1、先找出最小值元素,记录其元素值
2、初始i、j指向0下标
3、j下标依次往后遍历,如果j下标对应的元素是最小元素就跳过(j++),
如果不是最小元素,则把j下标对应的元素赋给i下标对应的元素,然后i、j都往后移动(i++、j++)
4、更新表长,即(L.length = i)。
bool deleteMin(SqList &L)
{
int minNum = INT_MAX; // 一开始把最小值元素赋值为int的最大值
for (int i = 0; i < L.length; i++) // 遍历顺序表,找到最小值
{
if (L.data[i] <= minNum)
{
minNum = L.data[i];
}
}
int i = 0, j = 0; // 初始i、j指向0下标
while (j < L.length)
{
if (L.data[j] == minNum) // 如果j下标对应的元素是最小元素就跳过(j++)
{
j++;
}
else // 如果不是最小元素,则把j下标对应的元素赋给i下标对应的元素,然后i、j都往后移动(i++、j++)
{
// 【注】下面三行可合并为:L.data[i++] = L.data[j++];
L.data[i] = L.data[j];
i++;
j++;
}
}
L.length = i; // 更新顺序表的长度
return true;
}
题目
已知长度为m的递增顺序表L1,长度为n的递增顺序表L2,其中L1的MaxSize>=m+n。请设计一个算法,合并L1、L2,使其成为一个新的递增顺序表
思路
1、依次遍历L2的元素,找到该元素在L1中应该插入的位置pos
2、L1从插入的位置开始到表尾,依次往后移动,然后插入L2的元素
3、更新L1的长度为L1的表长+L2的表长
int search(SqList L1, int key)
{
int i;
for (i = 0; i < L1.length; i++)
{
if (key < L1.data[i]) // 找到第一个比key大的元素停止
{
return i;
}
}
return i; // 如果key比L1中任意元素都大,则插入到L1.length的位置
}
void merge(SqList &L1, SqList L2)
{
int i, j, pos = 0;
for (i = 0; i < L2.length; i++)
{
pos = search(L1, L2.data[i]); // 找到L2.data[i]应该插入L1的下标
for (j = L1.length - 1; j >= pos; j--) // L1从插入的位置开始到表尾,依次往后移动
{
L1.data[j + 1] = L1.data[j];
}
L1.data[pos] = L2.data[i]; // 然后插入L2的元素
}
L1.length += L2.length; // 更新L1的表长
}
题目
请设计一个算法,删除带表头单链表的最小元素结点(假设唯一)。
思路
设置四个指针,分别是工作指针p以及他的前驱指针pre,指向最小值结点的指针minp以及他的前驱指针minpre
1、初始,pre和minpre指向头结点,p和minp指向第一个节点
2、依次遍历第二个结点到最后一个指针,如果p指向的结点数据域小于minp的数据域,则更新标记minp以及他的前驱minpre
3、遍历结束后,利用minpre结点来删除最小值结点
void deleteMin(LinkList &L)
{
LNode *pre = L, *p = pre->next; // pre和minpre指向头结点,p和minp指向第一个结点
LNode *minpre = pre, *minp = p;
while (p != NULL) // 遍历条件:p不为空
{
if (p->data < minp->data) //如果p指向的结点数据域小于minp的数据域,则更新标记minp以及他的前驱minpre
{
minp = p;
minpre = pre;
}
pre = p; // 更新pre指针以及p指针
p = pre->next;
}
minpre->next = minp->next; // 删除最小值结点
free(minp);
}
题目
L1、L2是两个递增的单链表,把L1、L2合并成一个递减的单链表。只能使用L1、L2上的结点(即不能新创建一个单链表)。
思路
设置两个指针p、q分别指向L1和L2的第一个结点,临时存储指针tmp
1、把L1置空
2、比较p、q指向的结点的数据域大小,数据域小的那个记录到临时指针tmp中。再利用头插法把结点插入L1(头插法每次插入最小的元素,整体就是递减的)
3、还未插入完成的链表继续插入
void mergeList(LinkList &L1, LinkList &L2)
{
LNode *p = L1->next;
LNode *q = L2->next;
LNode *tmp;
L1->next = NULL; // 把L1置空
while (p != NULL && q != NULL)
{
if (p->data <= q->data) // 如果p指向结点小于等于q指向的结点,则把该结点头插到L1中
{
tmp = p; // r记录p的位置,防止断链
p->next = L1->next; // 插入操作
L1->next = p;
p = tmp->next; // 更新p的位置
}
else // 如果q指向的结点小,则把该结点头插到L1中
{
tmp = q; // r记录q的位置,防止断链
q->next = L1->next;
L1->next = q;
q = tmp->next; // 更新q的位置
}
}
while (p != NULL)
{
tmp = p; // r记录p的位置,防止断链
p->next = L1->next; // 插入操作
L1->next = p;
p = tmp->next; // 更新p的位置
}
while (q != NULL)
{
tmp = q; // r记录q的位置,防止断链
q->next = L1->next;
L1->next = q;
q = tmp->next; // 更新q的位置
}
}
拓展
其他条件同该题目
变1:两个递增,合并成一个递增:每次对比得到最小值,尾插法插入L1
变2:两个递减,合并成一个递减:每次对比得到最大值,尾插法插入L1
变3:两个递减,合并成一个递增:每次对比得到最大值,头插法插入L1
思路:
把顺序表的表头当作比较元素,依次和第二个开始到最后一个元素进行比较,比较得出最小值,并记录最小值的下标,最后再把最后一个元素填补到删除的元素位置
自己的代码
int deleteMin(SqList &l)
{
if (!l.length) // 如果顺表表为空,报错
return ERROR;
int minNum = l.data[0]; // 先假设第一个元素是最小值,并记录对应的下标
int pos = 0;
for (int i = 1; i < l.length; i++) { // 从第二个元素开始,依次比最小值比较
if (l.data[i] < minNum) { // 当前值比最小值小,则更新最小值。
minNum = l.data[i];
pos = i;
}
}
l.data[pos] = l.data[l.length - 1]; // 空出的位置由最后一个元素填补,并且顺序表的长度减一
l.length--;
return minNum;
}
参考答案
思路:
空间复杂度要求是O(1),考虑第一个元素和最后一个元素互换,第二个元素与倒数第二个元素互换,依此类推
自己代码
bool reverseList(SqList &l)
{
for (int i = 0; i < (l.length / 2); i++) // 只需要循环一半
{
int temp = l.data[i];
// 0下标与l.length - 0 - 1互换,1下标和l.length - 1 - 1互换
l.data[i] = l.data[l.length - i - 1];
l.data[l.length - i - 1] = temp;
}
}
参考答案
要求时间复杂度为O(n)、空间复杂度为O(1)
用一个计算器count记录当前元素之前已经存在多少个x个,如果当前元素是x,则count++,否则当前元素往前移动count位。最后顺序表长度减去x的个数
自己的代码
void deleteX(SqList &l, int x)
{
int count = 0; // 记录已经存在多少个X个
for (int i = 0; i < l.length; i++)
{
if (l.data[i] == x) // 当前元素是x,则计算器++
{
count++;
}
else // 当前元素不是x,则当前元素往前移动count位
{
l.data[i - count] = l.data[i];
}
}
l.length -= count; // 顺序表最终的长度减去x的个数
}
思路:
思路1:
1、构建辅助顺序表Flag,用来存储每个元素出现的次数。
2、用计数器count计算值重复的元素的个数
3、如果当前元素已经存在(即重复出现),则Flag数组元素加一,count++。否则,把当前元素往前移动count位
4、最后顺序表长度减去count
思路2:
参考答案的思路
自己的代码
void deleteRepeat(SqList &l, SqList flag, int x)
{
int count = 0;
for (int i = 0; i < l.length; i++)
{
if (flag.data[l.data[i]] >= 1) // 当前元素重复出现
{
count++; // 值重复出现的个数加一
}
else
{
l.data[i - count] = l.data[i]; // 当前元素往前移动count位
}
flag.data[l.data[i]] ++; // 每个元素出现的次数加一
}
l.length -= count;
}
参考答案
思路
1、递归的结束条件是:L的next为空,当前结点已经为空
2、如果当前递归的结点数据域不是x,则进入下一层递归
3、如果当前递归的结点数据域是x,则进行删除操作
void delete_x(LinkList &L, int x)
{
LNode *p; // p指向待删除的结点
if (L == NULL) // 递归结束条件
{
return;
}
if (L->data == x) // 如果当前递归的结点数据域是x,则进行删除操作
{
p = L; // 记录要删除的结点,后面会释放p
L = L->next; // 删除当前L指向的结点
free(p);
delete_x(L, x); // 因为当前结点已经删除,L已经指向的是下一个结点。故递归参数不是L->next,与下一个递归参数做对比
}
else // 当前递归的结点数据域不是x,则进入下一层递归
{
delete_x(L->next, x); // 递归调用下一个结点
}
}
思路
设计两个指针,工作指针p和其前驱pre,删除指针tmp
1、初始,p指向第一个结点,pre指向头结点
2、顺序遍历链表,如果当前结点的数据域不是x,则pre、p往后移动。如果是x,则用pre指向的结点进行删除操作
自己的代码
void delete_x(LinkList &L, int x)
{
LNode *p = L->next, *pre = L, *tmp; // 初始,p指向第一个结点,pre指向头结点
while (p != NULL) // 如果p指向的结点不为空
{
if (p->data != x) //如果当前结点的数据域不是x,则pre、p往后移动
{
pre = p; // pre移动到他的后继p
p = pre->next; // p移动到他的后继pre.next
}
else
{
tmp = p; //tmp指向被删除的结点
p = p->next; // 更新p指针
pre->next = p; // 删除当前结点
free(tmp); // 释放空间
}
}
}
参考答案
思路
设置四个指针, 工作指针p以及指向它前驱的指针pre,最小值指针minp以及指向它前去的指针minpre
1、初始,p和minp指向第一个结点,pre和minpre指向头结点
2、从第一个结点顺序遍历,如果当前结点的数据域
LinkList delete_x(LinkList &L)
{
LNode *p = L->next, *pre = L; // 初始,p和minp指向第一个结点,pre和minpre指向头结点
LNode *minp = p, *minpre = pre;
while (p != NULL)
{
if(p->data < minp->data) //如果当前结点的数据域
{
minp = p;
minpre = pre;
}
// 每次判断结束后让p、pre同时向后移动
pre = p;
p = pre->next;
}
minpre->next = minp->next; // 删除最小值结点
free(minp);
return L;
}
参考答案
思路
要求辅助空间复杂度为O(1),表明不能另外创建一个单链表
设置一个工作指针p、一个临时指针tmp
1、初始,p指向第一个结点,头结点指向null,与链表断开
2、tmp暂存p的后继,把p头插法插入L中。再更新p的位置为其后继的位置
LinkList reverse(LinkList &L)
{
LNode *p = L->next, *tmp; // 初始,p指向第一个结点,头结点指向null,与链表断开
L->next = NULL;
while (p != NULL)
{
tmp = p->next; // tmp暂存p的后继
p->next = L->next; // 头插法
L->next = p;
p = tmp; // 更新p的位置为其后继的位置
}
return L;
}
参考答案
思路
公共结点:两个单链表有公共结点,即两个链表从某个结点开始,他们的next都指向同一个结点。
思路:
从两个链表的链尾开始,往前遍历到短一点的长度都是一样的
1、首先获取两个链表的长度len1、len2,比较长度。长链表记为longList,短链表记为shortList
2、让长链表顺序遍历(len1-len2)的绝对值个结点,目的是:让两个链表的长度相同
3、顺序遍历链表,如果两个链表的结点相同则返回链表,否则继续遍历。
int get_list_length(LinkList L) // 获取链表的长度
{
int length = 0;
while (L->next != NULL)
{
length++;
L = L->next;
}
return length;
}
LinkList find_same_list(LinkList &L1, LinkList &L2)
{
int len1, len2, dist;
LinkList longList, shortList;
len1 = get_list_length(L1); // 获取链表的长度
len2 = get_list_length(L2);
if (len1 >= len2) // 把长的链表赋给longList,短的链表赋给shortList
{
longList = L1;
shortList = L2;
dist = len1 - len2;
}
else
{
longList = L2;
shortList = L1;
dist = len2 - len1;
}
while (dist--) // 让长链表变得和短链表一样长
{
longList = longList->next;
}
while (longList != NULL) // 寻找公共结点
{
if (longList == shortList) // 找到第一个公共结点
return longList;
else // 当前结点不是公共结点,则继续往下找
{
longList = longList->next;
shortList = shortList->next;
}
}
return NULL; // 未找到公共结点
}
参考答案
思路
设置两个指针i,j,创建一个单链表C
1、初始时i指向A表,j指向B表
2、每次对比i、j指向结点的数据域大小,如果相同则创建一个新结点,把数据域赋值并且插入到C中。如果不相同,让数据域小的指针后移
参考答案
思路
参考答案