渣渣渣变渣渣系列(7)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jeffscott/article/details/70135680

一、题目描述:


二、算法思想:

考虑到遍历链表的时间开销比较大,题目中仅要求实现最大的时间效率,所以可以采用用空间换时间的方法,具体操作就是用一个数组存储已经出现的数字,这样只需要遍历一次链表即可。

三、核心代码:

struct Link*rmv_cf(struct Link*str1,int n)
{
  int i;
  int j=1;
  int A[n+1];//定义一个数组用来存储所有可能的值
  int p;//用来标志是否在记录数组中命中记录
  Link*start=str1;//保存链表指针
  Link*tmp=NULL;//用来暂存要删除的节点指针
  A[0]=str1->data;//第一个值不可能重复,直接加入A[]。
  while(str1->link!=NULL)//遍历链表,同时对每个节点在A[]中查找,当前指针不是指向最后节点则循环
  {             //如果存在则删除节点,不存在则将值加入A[]。
        p=0;//每次循环p更新为0
        for(i=0;i<n;i++)
      {
            if(str1->link->data==A[i])//在A[]中查找到值,删除节点。
            {
                tmp=str1->link;//暂存要删除的节点指针
                str1->link=str1->link->link;//将指针往后移动
                p=1;//将标志置为1,表示命中
                free(tmp);//释放该节点所占内存
                break;//停止在A[]中是匹配
            }
      }
      if(p==0)//如果未在A[]中查找到值,
        {
            A[j]=str1->link->data;//则将值添加到A[]
           // printf("A[%d]=%d\n",j,A[j]);
            j++;
            //将指针往后移动
            if(str1->link!=NULL)//确定当前指针不是指向最后节点,可以移动
            str1=str1->link;//链表指针往后移动
            else//当前指针已经指向最后一个节点,无法往后移动,也无需循环,直接退出
            break;//细心的亲可能会问,为啥不判断最后一个节点呢?
                  //其实我们在匹配的时候是使用if(str1->link->data==A[i]),
                  //不是使用str1->data,也就说当指针指向倒数第二个节点时
                  //就在判断最后一个节点的数据是否在A[]中匹配,为啥要这样?
                  //因为我们这是单链表,如果我们使用str1->data判断当前节点,
                  //是无法通过改变指针,直接实现删除的,因为当前的节点的前驱
                  //无法直接读取,必须重新遍历。
        }
  }
  return start ;
}


四、完整代码:

#include<stdio.h>
#include<stdlib.h>
int listlen(struct Link*head);/*求链表长度函数*/
struct Link*rmv_cf(struct Link*str1,int n);/*找到共同后缀的起始地址*/
struct Link*AppendNode(struct Link*head,int c);/*在链表末尾添加一个节点*/
void DeleteMemory(struct Link*head);/*删除动态分配的内存空间*/
void DisplyLink(struct Link*head);/*遍历链表*/
/*定义链表节点*/
struct Link{
char data;
struct Link*link;
};

int main()
{
    int n;
    int i;
    int c;
    struct Link*str1=NULL;//定义str1的头节点指针
    //建立链表str1
    printf("please input the number of the node: ");
    scanf("%d",&n);
    printf("Please input the data:");
    for(i=0;i<n;i++)
    {
        scanf("%d",&c);
        str1=AppendNode(str1,c);//向head为头指针的链表末尾添加节点
    }
    printf("the List is:");
    DisplyLink(str1);//打印初始链表
    printf("After rmv_cp:");
    DisplyLink(rmv_cf(str1,n));//打印删去重复元素的链表
    //释放链表所占的内存
     DeleteMemory(str1);

    return 0;
}
/*求链表长度函数*/
int listlen(struct Link*head){
int len=0;
while(head->link!=NULL){
    len++;
    head=head->link;
}
     return len;
}
/*删除链表中的重复元素*/
struct Link*rmv_cf(struct Link*str1,int n)
{
  int i;
  int j=1;
  int A[n+1];//定义一个数组用来存储所有可能的值
  int p;//用来标志是否在记录数组中命中记录
  Link*start=str1;//保存链表指针
  Link*tmp=NULL;//用来暂存要删除的节点指针
  A[0]=str1->data;//第一个值不可能重复,直接加入A[]。
  while(str1->link!=NULL)//遍历链表,同时对每个节点在A[]中查找,当前指针不是指向最后节点则循环
  {             //如果存在则删除节点,不存在则将值加入A[]。
        p=0;//每次循环p更新为0
        for(i=0;i<n;i++)
      {
            if(str1->link->data==A[i])//在A[]中查找到值,删除节点。
            {
                tmp=str1->link;//暂存要删除的节点指针
                str1->link=str1->link->link;//将指针往后移动
                p=1;//将标志置为1,表示命中
                free(tmp);//释放该节点所占内存
                break;//停止在A[]中是匹配
            }
      }
      if(p==0)//如果未在A[]中查找到值,
        {
            A[j]=str1->link->data;//则将值添加到A[]
           // printf("A[%d]=%d\n",j,A[j]);
            j++;
            //将指针往后移动
            if(str1->link!=NULL)//确定当前指针不是指向最后节点,可以移动
            str1=str1->link;//链表指针往后移动
            else//当前指针已经指向最后一个节点,无法往后移动,也无需循环,直接退出
            break;//细心的亲可能会问,为啥不判断最后一个节点呢?
                  //其实我们在匹配的时候是使用if(str1->link->data==A[i]),
                  //不是使用str1->data,也就说当指针指向倒数第二个节点时
                  //就在判断最后一个节点的数据是否在A[]中匹配,为啥要这样?
                  //因为我们这是单链表,如果我们使用str1->data判断当前节点,
                  //是无法通过改变指针,直接实现删除的,因为当前的节点的前驱
                  //无法直接读取,必须重新遍历,这是实现这个算法的关键细节。
        }
  }
  return start ;
}
//函数功能:新建一个节点并添加到链表末尾,返回添加节点后的表头指针
struct Link*AppendNode(struct Link*head,int data)
{
    struct Link*p=NULL;
    struct Link*pr=head;
    p=(struct Link*)malloc(sizeof(struct Link));
    if(p==NULL)
    {
        printf("no enough memory to allocate!\n");
        exit(0);
    }

    if(head==NULL)
    {
        head=p;
    }
    else
    {
        while(pr->link!=NULL)
        {
            pr=pr->link;
        }
        pr->link=p;
    }
        p->data=data;
        p->link=NULL;
        return head;

}
//释放head指向链表中的所有节点所占用的内存
void DeleteMemory(struct Link*head)
{
    struct Link*p=head;
    struct Link*pr=NULL;
    while(p!=NULL)
    {
        pr=p;
        p=p->link;
        free(pr);
    }
}
//显示链表中所有节点的节点好和该节点中的数据项内容
void DisplyLink(struct Link*head)
{
    struct Link*p=head;
    int j=1;
    while(p!=NULL)
    {
        printf("->[%d--%d] ",j,p->data);
        p=p->link;
        j++;
    }
    printf("\n");
}


五、测试分析:

时间复杂度O(nlgn),空间复杂度O(n)


猜你喜欢

转载自blog.csdn.net/jeffscott/article/details/70135680