记录一下那些优化时间复杂度和空间复杂度的小题
1.顺序表
相关定义如下:
struct _seqlist{
ElemType elem[MAXSIZE];
int last;
};
typedef struct _seqlist SeqList;
(1)设计一个高效的算法,从顺序表L中删除所有值介于x和y之间(包括x和y)的所有元素(假设y>=x),要求时间复杂度为O(n),空间复杂度为O(1)。
void del_x2y(SeqList* L, ElemType x, ElemType y)
{
int i = 0, j = 0;
while (i <= L->last) {
if (!(L->elem[i] >= x && L->elem[i] <= y)) {
L->elem[j] = L->elem[i];
i++;
j++;
} else
i++;
}
L->last = j - 1;
}
(2)编写算法,在一非递减的顺序表L中,删除所有值相等的多余元素。要求时间复杂度为O(n),空间复杂度为O(1)。
void del_dupnum(SeqList* L)
{
int i = 0, j = 0;
while (i <= L->last) {
if (L->elem[j] == L->elem[j + 1] || (L->elem[j - 1] == L->elem[i]) || (L->elem[j] < L->elem[j - 1])) {
if (L->elem[j] == L->elem[i] || (L->elem[j - 1] == L->elem[i]))
i++;
else if (j == 0 || (L->elem[j] > L->elem[j - 1])) {
L->elem[++j] = L->elem[i];
} else
L->elem[j] = L->elem[i];
} else {
L->elem[j] = L->elem[i];
i++;
j++;
}
}
if (L->elem[j - 1] >= L->elem[j])
L->last = j - 1;
else
L->last = j;
}
这种删除多余重复的值。其实这也只是在说明元素的特性,但相比与第一例麻烦的是不能由题目直接得出具体化的表达式。这个题的代码我也调试了很久,在诸多情况下未能找到一种通法,只能不断地通过提示增加条件。如果有大佬知道怎么简化,欢迎交流。
(3)已知顺序表L中的数据元素类型为int。设计算法将其调整为左右两部分,左边的元素(即排在前面的)均为奇数,右边所有元素(即排在后面的)均为偶数,并要求算法的时间复杂度为O(n),空间复杂度为O(1)。
void odd_even(SeqList* L)
{
int i = 0, j = L->last, temp;
while (i < j) {
if (L->elem[i] % 2 == 1)
i++;
if (L->elem[j] % 2 == 0)
j--;
if (i >= j)
break;
if (L->elem[i] % 2 == 0 && L->elem[j] % 2 == 1) {
temp = L->elem[i];
L->elem[i] = L->elem[j];
L->elem[j] = temp;
}
}
}
以上例子总结:类似于设计一个高效的算法,从顺序表L中删除所有值为x的元素,并要求算法的时间复杂度为O(n),空间复杂度为O(1)。i 记录原表的位置;j 记录新表将要放数据的位置 若L.data[i] != x,则让L.data[j]=L.data[i],然后i++,j++;若L.data[j] == x,则j++,继续向后找,直到找到值非x的元素并将其移入到L.data[i],然后i++,j++;直到表末尾。
void delx(SeqList &L, ElemType x) {
int i, j ;
i=0;
j=0;
while(i<L.length)
if(L.data[i]!=x){
L.data[j]=L.data[i]; /* 建立没有 x 的新表 */
i++;
j++;
}
else i++; /* 过滤掉 x 的数据项 */
L.length-=j;
}
这种为了提高效率一般会动用多个变量记录数组下标的变化。因为无论如何至少会遍历一遍数组。其中无论是删除特定的x ,或是在某个范围之间的值,这只是说明了某个元素是否满足题目条件。
2.链表
相关定义如下:
struct _lnklist{
ElemType data;
struct _lnklist *next;
};
typedef struct _lnklist Node;
typedef struct _lnklist *LinkList;
(1)已知线性表中的元素(整数)以值递增有序排列,并以单链表作存储结构。试写一高效算法,删除表中所有大于mink且小于maxk的元素(若表中存在这样的元素),分析你的算法的时间复杂度。
void lnk_del_x2y(LinkList L, ElemType mink, ElemType maxk)
{
Node *p = L->next, *prev = L, *k = L; //尽量不用p=p->next来作为判断条件,容易造成程序运行超时
for (; p->next;) {
if (p->data > mink && p->data < maxk) {
prev = p;
p = p->next;
free(prev);
} else if (p->data <= mink) {
k = p; //保留k
p = p->next;
} else if (p->data >= maxk)
break;
else
continue;
}
k->next = p;
}
在这个例子中的五种情况下,我找到了一种通法。
假如mink=3,maxk=7 则有以下五种情况:
1,2,3,4; 1,2,3;4,5,6;1,2,3,4,5,6,7,8;6,7,8
(2)已知一个带有表头结点的单链表, 假设链表只给出了头指针L。在不改变链表的前提下,请设计一个尽可能高效的算法,查找链表中倒数第k个位置上的结点(k为正整数)。若查找成功,函数通过指针参数 p_ele 返回该结点 data 域的值,此时函数返回 1;否则,函数返回 0。
int lnk_search(LinkList L, int k, ElemType* p_ele)
{
Node *p = L, *n = L;
int count = 0;
while (p) {
p = p->next;
count++;
if (count >= k) {
n = n->next;
}
}
*p_ele = n->data;
if (n == L)
return 0;
else
return 1;
}
总结:
(3)设线性表A=(a1, a2,…,am),B=(b1, b2,…,bn),试写一个按下列规则合并A、B为线性表C的算法,使得:C= (a1, b1,…,am, bm, bm+1, …,bn) 当m≤n时;或者C= (a1, b1,…,an, bn, an+1, …,am) 当m>n时。线性表A、B、C均以单链表作为存储结构,且C表利用A表和B表中的结点空间构成。注意:单链表的长度值m和n均未显式存储。即将A和B合并为C,其中 C 已经被初始化为空单链表
void lnk_merge(LinkList A, LinkList B, LinkList C)
{
Node* pa = A->next;
Node* pb = B->next; //pa、pb当前节点
Node* pc = C; //借用表La的头结点作为表Lc的头结点
while ((pa != NULL) && (pb != NULL)) {
pc->next = pa;
pc = pa;
pa = pa->next;
pc->next = pb;
pc = pb;
pb = pb->next;
}
if (pa != NULL)
pc->next = pa;
else
pc->next = pb;
free(B);
free(A); //将Lb的表头释放
}
总结:链表问题尽量不要用p=p->next来作为条件、循环等语句判断条件,容易造成程序运行超时(你永远也不知道哪种情况会导致p=p->next不存在,有可能这种情况满足你的代码,但当你没考虑到的情况可能会使程序崩溃)。
入坑不久,水平有限。还请各路大神多加指教。