• 数据结构自学笔记 链表超进化


    讲真,链表是个十分入门且简单的数据结构,虽然用的不多,但有时候还真非它莫属。

    然而,作为一名C++选手,我的链表总是被各种大常数以及难写的代码困扰。

    今天从算法导论上学来了点小技巧,赶紧代码实现了一下。

    之前,我的链表都是这么写的。

    class list
    {
        struct node
        {
            int key;
            node *next;
            node *prev;
        };
        
        node *head;
        node *tail;
        
        list(void)
        {
            head = NULL;
            tail = NULL;
        }
        
        void insert_to_head(int k)
        {
            node *t = new node;
            
            t -> key = k;
            
            t -> prev = NULL;
            t -> next = head;
            
            if (head != NULL)
                head -> prev = t;
            
            head = t;
        }
        
        void insert_to_tail(int k)
        {
            node *t = new node;
            
            t -> key = k;
            
            t -> prev = tail;
            t -> next = NULL;
            
            if (tail != NULL)
                tail -> next = t;
                
            tail = t;
        }
        
        void delete_a_node(node *t)
        {
            if (t -> prev != NULL)
                t -> prev -> next = t -> next;
            else
                head = t -> next;
                
            if (t -> next != NULL)
                t -> next -> prev = t -> prev;
            else
                tail = t -> prev;
                
            delete(t);
        }
        
        node *find_the_key(int k)
        {
            node *t = head;
            
            while (t != NULL && t -> key != k)
                t = t -> next;
                
            return t;
        }
    };

    代码还是蛮好看的,自认为。然而接连不断地新建结点的常数总是让人吃不消。

    于是,从Introduction to Algorithm中学来的第一招——用哨兵(哑结点)优化常数以及代码难度。

    原来,我们经常用到一个表示空结点的NULL,而且为了防止非法访问,还要各种边界特判,烦不胜烦。

    现在,我们新建一个名为NIL的哑结点,用来替代原来的所有NULL,省去了好多特判,代码也好写多了。重点是还能通过NIL把head和tail连在一起,形成了一个环状链表。

    class list
    {
        struct node
        {
            int key;
            node *next;
            node *prev;
        };
        
        node *nil;
        
        list(void)
        {
            nil = new node;
            
            nil -> key = -1;
            nil -> next = nil;
            nil -> prev = nil;
        }
        
        void insert_to_head(int k)
        {
            node *t = new node;
            
            t -> key = k;
            
            t -> prev = nil;
            t -> next = nil -> next;
            t -> next -> prev = nil -> next = t;
        }
        
        void insert_to_tail(int k)
        {
            node *t = new node;
            
            t -> key = k;
            
            t -> next = nil;
            t -> prev = nil -> prev;
            t -> prev -> next = nil -> prev = t;
        }
        
        void delete_a_node(node *t)
        {
            t -> prev -> next = t -> next;
            t -> next -> prev = t -> prev;
                
            delete(t);
        }
        
        node *find_the_key(int k)
        {
            node *t = nil -> next;
            
            while (t != nil && t -> key != k)
                t = t -> next;
                
            return t;
        }
    };

    后来,看到算法导论还说我们可以自己手写新建结点和释放结点函数,实现垃圾回收,感觉会快很多呢。我就顺便扩展了一下,实现了自动申请新的更多的空间。

    class list
    {
        struct node
        {
            int key;
            node *next;
            node *prev;
        };
        
        const static int lim = 1000000;
        
        node *nil;
        node *fre;
        
        void apply_more(void)
        {
            node *lst = new node[lim];
            
            for (int i = 0; i < lim; ++i)
            {
                lst[i].next = fre;
                fre = lst + i;
            }
        }
        
        node *new_node(void)
        {
            if (fre == NULL)
                apply_more();
                
            node *ret = fre;
            fre = fre -> next;
            
            return ret;    
        }
        
        void free_node(node *t)
        {
            t -> next = fre;
            fre = t;
        }
        
        list(void)
        {
            fre = NULL;
            
            nil = new_node();
            
            nil -> key = -1;
            nil -> next = nil;
            nil -> prev = nil;
        }
        
        void insert_to_head(int k)
        {
            node *t = new_node();
            
            t -> key = k;
            
            t -> prev = nil;
            t -> next = nil -> next;
            t -> next -> prev = nil -> next = t;
        }
        
        void insert_to_tail(int k)
        {
            node *t = new_node();
            
            t -> key = k;
            
            t -> next = nil;
            t -> prev = nil -> prev;
            t -> prev -> next = nil -> prev = t;
        }
        
        void delete_a_node(node *t)
        {
            t -> prev -> next = t -> next;
            t -> next -> prev = t -> prev;
                
            free_node(t);
        }
        
        node *find_the_key(int k)
        {
            node *t = nil -> next;
            
            while (t != nil && t -> key != k)
                t = t -> next;
                
            return t;
        }
    };

    @Author: YouSiki

  • 相关阅读:
    redis使用watch完成秒杀抢购功能:
    OAUTH协议
    常用mysql命令大全
    版本控制器 (Svn,Git)
    vue axios上传文件实例
    vue-resource 和 axios的区别
    js递归算法1+ 2+3.....100的和
    vue-cli title 里面怎动态显示文字
    Entity Framework With Mysql 之Code First
    关于a标签下的img元素在IE7下不能点击的问题
  • 原文地址:https://www.cnblogs.com/yousiki/p/6095981.html
Copyright © 2020-2023  润新知