• 【算法】插入排序 insertion_sort


    准备写个《STL 源代码剖析》的读书笔记,开个专栏。名为《STL 的实现》,将源代码整理一遍。非常喜欢侯捷先生写在封底的八个字:天下大事。必作于细。他在书中写到:“我开玩笑地对朋友说,这本书出版,给大学课程中的「数据结构」和「算法」两门授课老师出了个难题。差点儿全部可能的作业题目(复杂度证明题除外),本书都有了详尽的解答。

    然而,假设学生可以从庞大的SGI STL源代码中干净抽出某一部份,加上自己的包装,做为呈堂作业,也足以证明你有资格获得学分和高分。其实。追踪一流作品并于当中吸取养份,远比自己关起门来写个三流作品,价值高得多—我的确觉得99.99%的程序猿所写的程序,在SGI STL面前都是三流水平。

    ”有的人说仅仅需把 STL 的那些接口弄清楚即可了。没有必要看它是怎样实现的,我觉得你要是不看的话真的太可惜了,而侯先生则觉得“从技术研究与本质提升的角度看。深究细节能够让你彻底掌握一切;不论是为了重温数据结构和算法,或是想要扮演轮子角色。或是想要进一步扩张别人的轮子,都可因此获得深厚扎实的基础。

    本专栏分为【容器】、【算法】、【迭代器】、【分配器】、【适配器】、【仿函数】等几个部分。第一篇解说 STL 内置的一个算法:插入排序,它将用于 std::sort 中。

    普通的插入排序算法例如以下:

    typedef int Type;	// 程序中用到的 Type 都是由 traits 得来,这里简化了
    
    template <class RandomAccessIterator>
    void insertion_sort(RandomAccessIterator first, RandomAccessIterator last) {
    	if (first == last) {
    		return ;
    	}
    
    	RandomAccessIterator p;
    	for (RandomAccessIterator ite = first + 1; ite != last; ++ite) {
    		Type tmp = *ite;
    		for (p = ite; p != first && tmp < *(p - 1); --p) {
    			*p = *(p - 1);
    		}
    		*p = tmp;
    	}
    }

    当中内循环的终止条件中有一关系表达式 p != first 用于推断是否越界。而在 STL 的实现中为了减小这一开销,在以下的 linear_insert 函数的開始就检測尾是否小于头,若不成立,就表明要插入的元素一定不会排在最前面,所以在后面的移动元素过程中就不用作越界检查了,它的作用类似于“哨兵”。STL 中插入排序的实现例如以下:

    /********************************************************************
    created:	2014/04/21 22:03
    filename:	insertion_sort.cpp
    author:		Justme0 (http://blog.csdn.net/justme0)
    
    purpose:	insertion sort
    *********************************************************************/
    
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    
    typedef int Type;	// 程序中用到的 Type 都是由 traits 得来。这里简化了
    
    /*
    ** 将[first, last)拷到[result-(last-first), result)。从后向前复制
    ** 这里简化了 std::copy_backward
    */
    template <class T>
    inline T * copy_backward(const T *first, const T *last, T *result) {
    	const ptrdiff_t num = last - first;
    	memmove(result - num, first, sizeof(T) * num);
    	return result - num;
    }
    
    /*
    ** 将 value 插到 last 前面(不包含 last)的区间
    ** 此函数保证不会越界(主调函数已推断),因此以 unguarded_ 开头
    */
    template <class RandomAccessIterator, class T>
    void unguarded_linear_insert(RandomAccessIterator last, T value) {
    	RandomAccessIterator next = last;
    	--next;
    	for (; value < *next; --next) {
    		*last = *next;
    		last = next;
    	}
    	*last = value;
    }
    
    /*
    ** 将 last 处的元素插到[first, last)的有序区间
    */
    template <class RandomAccessIterator>
    void linear_insert(RandomAccessIterator first, RandomAccessIterator last) {
    	Type value = *last;
    	if (value < *first) { // 若尾比头小,就将整个区间一次性向后移动一个位置
    		copy_backward(first, last, last + 1);
    		*first = value;
    	} else {
    		unguarded_linear_insert(last, value);
    	}
    }
    
    template <class RandomAccessIterator>
    void insertion_sort(RandomAccessIterator first, RandomAccessIterator last) {
    	if (first == last) {
    		return ;
    	}
    
    	for (RandomAccessIterator ite = first + 1; ite != last; ++ite) {
    		linear_insert(first, ite);
    	}
    }
    
    int main(void) {
    	Type arr[] = {3, 1, 5, 2, 0, 4};
    	int size = sizeof arr / sizeof *arr;
    
    	insertion_sort(arr, arr + size);
    
    	for (Type *ite = arr; ite != arr + size; ++ite) {
    		printf("%d ", *ite);	// 0 1 2 3 4 5
    	}
    
    	system("PAUSE");
    	return 0;
    }

    能够看到,实现中将这个算法多次抽象,这是由于有的函数譬如 unguarded_insertion_sort 将用于其它算法,便于代码重用。

    STL 的源代码编写是非常有讲究的,比方上面的 value < *first 这个表达式,可不能写成 *first > value,由于迭代器所指的对象未必然义了大于这个操作,标准中规定要排序。client必须保证小于号的定义(或是传入仿函数作为比較标准)。所以源代码中就仅仅用小于来推断。



  • 相关阅读:
    vue强制更新$forceUpdate()
    js数组拼接成字符串,去除最后一个逗号
    JavaScript数组遍历:for、foreach、for in、for of、、().each的区别
    json.stringify()的妙用,json.stringify()与json.parse()的区别
    第四次博客作业-结对项目
    于达——第九次作业
    于达——第八次作业
    软件工程第三次作业——关于软件质量保障初探
    于达——第七次作业
    于达——第六次作业
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5173591.html
Copyright © 2020-2023  润新知