本份代码练习册来自B站up:白话拆解数据结构!
今日题目为:
(1)删除顺序表中的最小值(唯一),返回被删的值,删除的地方由最后一个元素补上
(2)删除顺序表中值重复的元素
因为要一个月刷完,每天大概要2-3题的样子。基本是按顺序刷的(除了今天)。
第一题:删除顺序表中的最小值(唯一),返回被删的值,删除的地方由最后一个元素补上
结构体定义:顺序表的后面都不再单独说了。看day1https://blog.csdn.net/m0_61522765/article/details/141350324?spm=1001.2014.3001.5502
算法思想:
因为最小值是唯一的,所以要先找到这个值,而这个值是min,需要比较出来才知道它是不是最小的。
我们可以将第一个元素拷贝一份存到temp变量中,然后和第二个元素比较,如果第二个元素比temp小,就更新temp中的值,如果第二个元素比temp大,就继续往下找...直到找到表尾,最后的temp就是我们需要的最小值了,找到这个值的时候,需要记录下它的下标,因为顺序表本质上是个数组,根据数组下标很容易就能找到这个元素(随机访问),然后再执行删除操作。
ElemType min = L.data[0];
int pos=0;
for(int i=1;i<L.length;i++)
{
if (L.data[i] <= min){
min = L.data[i];
pos = i;
}
}
value = min; //返回被删的值
题目中还要求删除的地方由最后一个地方补上,其实就是将最后一个元素赋值给最小值处,再L.length-1即可。即使原来的L.data[length-1]处仍然还是表尾元素,但我们表长减了一个1,它不在我们表的范围了,里面的数据是多少我们不关心(就本题而言)!
L.data[pos] = L.data[L.length-1];
L.length--;
实操一下:成功!
完整代码如下:(在VScode环境下已成功运行)
#include <iostream>
#include <cstdio>
#include <ctime>
using namespace std;
// 删除顺序表中的最小值(唯一),返回被删的值,删除的地方由最后一个元素补上
typedef int ElemType;
struct SqList {
ElemType data[100];
int length;
};
bool Delete_Min(SqList &L,ElemType &value) {
if(L.length == 0) return false;
ElemType min = L.data[0];
int pos=0;
for(int i=1;i<L.length;i++)
{
if (L.data[i] <= min){
min = L.data[i];
pos = i;
}
}
value = min;
L.data[pos] = L.data[L.length-1];
L.length--;
return true;
}
int main() {
SqList L;
L.length = 6;
srand(static_cast<unsigned int>(time(nullptr)));
// 随机赋值
for(int i =0;i<L.length;i++){
L.data[i] = rand()%40;
}
printf("Original list: ");
for (int i = 0; i < L.length; i++) {
cout << L.data[i] << " ";
}
cout << endl;
ElemType minValue;
if (Delete_Min(L, minValue)) {
cout << "The minimum value is: " << minValue << endl;
cout << "The new list is: ";
for (int i = 0; i < L.length; i++) {
cout << L.data[i] << " ";
}
cout << endl;
} else {
cout << "The list is empty." << endl;
}
return 0;
}
第二题:删除顺序表中值重复的元素
法一:比较容易想到的是先对这个表排个序,值重复的元素不就挨着了嘛。(后面想了一下,其他元素的位置全变了,这种解法应该不行。这里提供一种思路的改进:原方法不变,将sort加进del里,sort里面的L不加引用,然后得到的是排完序后且无重复值的表,将这个表与原表对比,找出不重复值的下标记录下来,放进这个新表就行了。可以自己尝试一下,这种方法应该是需要两个表)
// 不能之间写swap,因为c++标准库里有swap,会定义冲突
void swap_elem(SqList &L, int i, int j){
int tmp = L.data[i];
L.data[i] = L.data[j];
L.data[j] = tmp;
}
void sort(SqList &L){ // 冒泡排序
//从i=N-1倒回来调度,这是每次固定右边已经排序好的数组
int N = L.length;
for (int i = N - 1; i > 0; i--) {//i不能到0位置,因为下面依赖有0+1
//然后每次从0--i来冒泡
for (int j = 0; j < i; j++) {//不能等于i哦,因为依赖的是j+1
if (L.data[j] > L.data[j + 1])
swap_elem(L, j, j + 1);
}
}
}
排完序后,由于重复的元素是挨着的,但是有几个元素重复了你不知道,如果选择找到值重复的元素并删除他不大好做,需要像122333455这样的表,判断的次数就太多了。沿用昨天的代码思想,我们可以反着想,找重复的不好找,找不重复的不就行了吗!
bool del_(SqList &L){ //与昨天那两题差不多
if(L.length==0) return false;
int j=0; //j的作用与昨天的一致
for(int i=0;i<L.length;i++)
{
if(L.data[i]!=L.data[i+1])
{
L.data[j]=L.data[i];
j++;
}
}
L.length = j;//同样无须修正
return true;
}
实操:成功!
完整代码如下:(在vscode环境下已成功运行)
#include <iostream>
#include <cstdio>
#include <ctime>
using namespace std;
// 删除顺序表中值重复的元素
// 法一:比较容易想到的是对顺序表进行排序,值重复的必定连续。该方法时间复杂度取决于排序的复杂度。
// 如果采用的是快速排序,那么时间复杂度是O(nlogn);本解法用的是冒泡排序。
typedef int ElemType;
struct SqList {
ElemType data[100];
int length;
};
// 不能之间写swap,因为c++标准库里有swap,会定义冲突
void swap_elem(SqList &L, int i, int j){
int tmp = L.data[i];
L.data[i] = L.data[j];
L.data[j] = tmp;
}
void sort(SqList &L){
//从i=N-1倒回来调度,这是每次固定右边已经排序好的数组
int N = L.length;
for (int i = N - 1; i > 0; i--) {//i不能到0位置,因为下面依赖有0+1
//然后每次从0--i来冒泡
for (int j = 0; j < i; j++) {//不能等于i哦,因为依赖的是j+1
if (L.data[j] > L.data[j + 1])
swap_elem(L, j, j + 1);
}
}
}
bool del_(SqList &L){
if(L.length==0) return false;
int j=0; //j的作用与day4一致
for(int i=0;i<L.length;i++)
{
if(L.data[i]!=L.data[i+1])
{ L.data[j]=L.data[i];
j++;}
}
L.length = j;//同样无须修正
return true;
}
int main() {
SqList L1,L2;
L1.length =10;
srand(static_cast<unsigned int>(time(nullptr)));
// 随机赋值
for(int i =0;i<L1.length;i++){
L1.data[i] = rand()%5; //必定能有重复值
}
cout << "Original list: ";
for (int i = 0; i < L1.length; i++) {
cout << L1.data[i] << " ";
}
cout << endl;
sort(L1);
cout << "sorted list: ";
for (int i = 0; i < L1.length; i++) {
cout << L1.data[i] << " ";
}
cout << endl;
if (del_(L1)) {
cout << "The new list is: ";
for (int i = 0; i < L1.length; i++) {
cout << L1.data[i] << " ";
}
} else {
cout << "The list is empty." << endl;
}
return 0;
}
解法二:暴力破解(不排序)
也可以叫双指针法,i从表头开始遍历,j从i+1的位置开始遍历,如果L.data[i]和L.data[j]相等,就说明重复了,执行删除操作,将L.data[j]后面的元素往前移动一位,同时更新表长;如果不相等,就j继续往下找,直到找到相等的元素,如果一轮后没找到,说明这个元素是唯一的,就i更新,j又从i+1的位置找,重复这个过程直到i到达表尾!
//法二。暴力解法
bool new_list(SqList &L1)
{
int i,j,m=0;
// L2.data[j]=L1.data[i];
for(i=0;i<L1.length-1;i++)
{
for(j=i+1;j<L1.length;){
if(L1.data[i]==L1.data[j])
{ // 删除当前元素
for(int n=j;n<L1.length;n++)
L1.data[n]=L1.data[n+1];
L1.length--; //更新表长,同时循环条件也在更新
}
else j++; //不删除元素时才让j递增
}
}
return true;
}
证一下:成功!
完整代码如下:(vscode已跑通)
#include <iostream>
#include <cstdio>
#include <ctime>
using namespace std;
// 删除顺序表中值重复的元素
// 法一:比较容易想到的是对顺序表进行排序,值重复的必定连续。该方法时间复杂度取决于排序的复杂度。
// 如果采用的是快速排序,那么时间复杂度是O(nlogn);本解法用的是冒泡排序。
typedef int ElemType;
struct SqList {
ElemType data[100];
int length;
};
//法二。暴力解法
bool new_list(SqList &L1)
{
int i,j,m=0;
// L2.data[j]=L1.data[i];
for(i=0;i<L1.length-1;i++)
{
for(j=i+1;j<L1.length;){
if(L1.data[i]==L1.data[j])
{ // 删除当前元素
for(int n=j;n<L1.length;n++)
L1.data[n]=L1.data[n+1];
L1.length--; //更新表长,同时循环条件也在更新
}
else j++; //不删除元素时才让j递增
}
}
return true;
}
int main() {
SqList L1,L2;
L1.length =10;
L2.length =10;
srand(static_cast<unsigned int>(time(nullptr)));
// 随机赋值
for(int i =0;i<L1.length;i++){
L1.data[i] = rand()%5; //必定能有重复值
}
cout << "Original list: ";
for (int i = 0; i < L1.length; i++) {
cout << L1.data[i] << " ";
}
cout << endl;
if (new_list(L1)) {
cout << "The new list is: ";
for (int i = 0; i < L1.length; i++) {
cout << L1.data[i] << " ";
}
} else {
cout << "The list is empty." << endl;
}
return 0;
}