本节题目来自王道单科37页。说不定哪天就放弃了在电脑上敲代码了,好费时啊啊啊啊。

 

1、设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点

终止条件:F(L,x)不做任何事情;                                         若L为空表      

递归主体:F(L,x)删除*L结点;并递归下一层F(L->next,x)    若L->data==x

     递归下一层F(L->next,x)                                    其他情况     

void R_delete(LinkList &L,int x){
//设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点
    LNode *s;
    if(L==NULL)return;
    if(L->data==x){
        s=L;
        L=L->next;
        free(s);
        R_delete(L,x);
    }
    else{
        R_delete(L->next,x);
    }
} 

 

2、在带头结点的单链表L中,删除所有值为x的结点,并释放其空间,假设值为x的结点不唯一。

void del_x2(LinkList &L,int x){
    LNode *pre=L,*p=L->next;
    LNode *q;
    while(p!=NULL){
        if(p->data==x){
            q=p;
            pre->next=p->next;
            p=p->next;
free(q);
        }
        else{ 
            pre=p;
            p=p->next;
        }
    }
}

 

删除操作大总结:

①这里的删除操作是定义了一个新指针来指向要删除的结点,然后释放这个指针就相当于释放了它指向的结点。

      p是要删除的结点,pre是删除结点的前驱结点,定义一个新指针q指向要删除的结点。具体是下面这样:
q=p; pre->next=p->next; p=p->next; //这里只是p要继续往下遍历。r如果不用遍历可以不用到这句。 free(q);

②其实还有一种删除的方法,可以不用定义新指针。下面这段代码可以代替上面几行。且更常用。

        pre->next=p->next;

   free(p);

   p=pre->next;           //遍历也不会丢失地址,因为pre->next刚好有记到。如果不用遍历可以不用到这句。

 

③如果遍历的时候不在乎所删除结点的前驱结点要和后面连接起来,而可以边遍历边释放的话,甚至可以没有pre指针。

直接用q指向要删除的结点,p继续往后遍历,然后释放掉q就ok了。

q=p;   p=p->next;   free(q);  即可。这句和上面第一种只是少了前驱结点和后面的连接,即pre->next=p->next;

下面这段代码用到的就是这种情况。

void del_x3(LinkList &L,int x){
    LNode *p=L->next,*r=L,*q;
    while(p!=NULL){
        if(p->data!=x){
            r->next=p;
            r=p;       //尾插法的典型代码。r->next=p;r=p; 其中r=p可以用r=r->next代替,都表示r向后移动 
            p=p->next;  //继续遍历 
        }
        else{
            q=p;   //删除p所指结点,释放空间 
            p=p->next;
free(q);  
        }
    }
    r->next=NULL;
}

 

3、设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值

分析:①反向第一个想到栈,②进而想到递归。③当然也可以头插法,④或者改变链表指针的方向,即下面第5题。

void R_print(LinkList L){
    if(L->next!=NULL){
        R_print(L->next);
    }
    printf("%d ",L->data);
} 

 

4、在单链表L中删除一个最小值结点(假设最小值结点是唯一的)

分析:因为只删除一个结点,要考虑前驱结点,所以我们考虑上面的删除操作中的第二个,即pre->next=p->next;   free(p);

  找最小值类似于顺序表先定义一个min=第一个,然后往后遍历逐步比较,只要后面有就更新min值。因为此处是删除最小值结点,我们也用结点类型来定义,即LNode *minp;又因为删除的时候要注意前驱结点,则再设一个LNode *minpre,始终在minp前面。

void Min_Delete(LinkList &L){
    LNode *pre,*p;
    LNode *minpre,*minp;
    while(L->next!=NULL){
        pre=L;minpre=pre;
        p=L->next;minp=p;
        while(p!=NULL){
            if(p->data<minp->data){
                minpre=pre;       //更新minp,使其指向新找到的更小的结点
                minp=p;
            }
            pre=p;
            p=p->next;  //往后遍历
        }
        printf("%d ",minp->data);
        minpre->next=minp->next;
        free(minp);
    }    
}

 

这道题和王道第9题相似,区别只在于前者只删除一个结点,后者删除所有结点。所以后者在外层加上一个while(L->next!=NULL)

void Min_Delete(LinkList &L){
    LNode *pre,*p;
    LNode *minpre,*minp;
    while(L->next!=NULL){
        pre=L;minpre=pre;
p=L->next;minp=p;
while(p!=NULL){
            if(p->data<minp->data){
                minpre=pre;
                minp=p;
            }
            pre=p;
            p=p->next;
        }
        printf("%d ",minp->data);
        minpre->next=minp->next;
        free(minp);
    }    
}

 

 

5、编写算法将带头街的单链表“就地”逆置,即空间复杂度为O(1)

①头插法

LinkList Reverse1(LinkList &L){
    LNode *p,*q;
    p=L->next;
    L->next=NULL;   //容易忘记 ,要养成好习惯。 
    while(p!=NULL){
        q=p->next;   //用新的指针记住p的后继结点的位置,防止断链
        p->next=L->next;
   L->next=p;   //头插法的典型代码,要记住
        p=q;
    }
    return L;
}

②p每往后遍历就修改指针,将其next指向上一个指针,这个设一个新指针pre来记录,就像删除操作的时候一样。

LinkList Reverse2(LinkList L){
    LNode *pre,*p=L->next,*r=p->next;
    p->next=NULL;           //处理第一个结点,就像链表中的尾结点r->next=NULL。这是反转后的链表,也要这样处理。
    while(r!=NULL){           //如果r为空,则说明p为最后一个结点。
        pre=p;
        p=r;
        r=r->next;       //可理解为三个指针都向后移动
        p->next=pre;         //指针反转      
    }
    L->next=p;      //处理最后一个结点,把头结点指向最后结点,实现最终反转。
    return L;
}

 

posted on 2018-06-03 22:48  TinyRick  阅读(1180)  评论(0编辑  收藏  举报