数据结构基础(3) --Permutation & 插入排序
Permutation(排列组合)
排列问题:
设R = {r1, r2, ... , rn}是要进行排列的n个元素, Ri = R-{ri}; 集合X中元素的全排列记为Permutation(X), (ri)Permutation(X)表示在全排列Permutation(X)的每一个排列前加上前缀ri得到的排列.
R的全排列可归纳定义如下:
当n=1时,Permutation(R)={r},r是集合R中唯一的元素;
当n>1时,Permutation(R)由(r1)Permutation(R1),(r2)Permutation(R2), ..., (rn)Permutation(Rn)构成。
依次递归定义,则可设计产生Permutation(X)的递归算法如下:
template <typename Type> void permutation(Type *array, int front, int last) { //已经递归到了最后一个元素 if (front == last) { //打印输出 for (int i = 0; i < front; ++i) { cout << array[i] << ' '; } cout << array[front] << endl; return ; } else { for (int i = front; i <= last; ++i) { // 不断的从下标为[front ~ last]的元素中选择一个元素 //作为当前序列的开头元素 std::swap(array[front], array[i]); permutation(array, front+1, last); std::swap(array[front], array[i]); } } }
算法说明:
算法Permutation(array, k, m)递归地产生所有前缀是array[0:k-1],且后缀是array[k:m]的全排列的所有排列.函数调用(list, 0, n-1)则产生list[0:n-1]的全排列.
算法演示:
char str[] = “abc”;的递归调用过程如图:
小结:
虽然我们自己实现了Permutation, 但C++ STL中也实现了std::next_permutation算法, 在一般应用中, 我比较推荐使用STL中已经实现好的next_permutation, 毕竟STL的代码质量还是非常高的, 而且速度一点也不逊色于我们的实现;
插入排序
插入排序是低级排序中速度最快的一种(冒泡/选择/插入排序效率均为O(N^2)),但是跟快速排序(NlogN),归并排序(NlogN)还是有一定的差距的⊙﹏⊙b汗!
算法思想:
不断的从尚未排序的序列中选择一个元素插入到已经排好序的序列中(当然,会有一个选择插入位置的过程:选择一个位置, 该位置前的元素都比该元素小, 该位置后的元素都比该元素大),类似于现实生活中的斗地主的摸排过程.
//实现与解析 /**说明: outer:第一个未排序的元素 inner:搜索第一个小于tmp的元素的位置 tmp: 用于暂存第一个尚未排序的元素 */ template <typename Type> void insertionSort(Type *begin, Type *end) throw (std::range_error) { if ((begin == end) || (begin == NULL) || (end == NULL)) throw std::range_error("pointer unavailable"); //假设第一个元素已经排好序了 for (Type *outer = begin+1; outer < end; ++outer) { Type tmp = *outer; //暂存第一个未排序的元素 Type *inner = outer; //inner 不断寻找一个位置(*(inner-1) <= tmp), //使得tmp->*inner(tmp所保存的值插入到inner位置) while (inner > begin && *(inner-1) > tmp) { *inner = *(inner-1); //元素后移 -- inner; //指针前移 } *inner = tmp; //将元素插入已排序序列 } } template <typename Iter> void insertionSort(Iter begin, Iter end) { return insertionSort(&(*begin), &(*end)); }
/**insertionSort_2算法的由来: 可以使用*begin(序列的第一个元素)作为哨兵, 这样就可以省去insertionSort 中第15行的inner > begin判断, 但付出的代价是begin所指向的位置不能再存储有用的数据, 只能被用作排序的哨兵 -> 以空间换时间(个人感觉没什么必要...) */ template <typename Type> void insertionSort_2(Type *begin, Type *end) throw (std::range_error) { if ((begin == end) || (begin == NULL) || (end == NULL)) throw std::range_error("pointer unavailable"); for (Type *outer = begin+2; outer < end; ++outer) { *begin = *outer; Type *inner = outer; //因为*begin不可能 > *begin, 所以该循环一定会退出 while (*(inner-1) > *begin) { *(inner) = *(inner-1); --inner; } *inner = *begin; } }
附-permutation与std::next_permutation测试代码
int main() { vector<char> str; for (char ch = 'a'; ch <= 'c'; ++ch) str.push_back(ch); permutation(&(*str.begin()), 0, 2); cout << "----------" << endl; typedef vector<char>::iterator Iter_type; do { for (Iter_type iter = str.begin(); iter != str.end(); ++iter) cout << *iter << ' '; cout << endl; } while (std::next_permutation(str.begin(), str.end())); return 0; }
数据结构基础(3) --Permutation & 插入排序的更多相关文章
- 算法与数据结构基础 - 哈希表(Hash Table)
Hash Table基础 哈希表(Hash Table)是常用的数据结构,其运用哈希函数(hash function)实现映射,内部使用开放定址.拉链法等方式解决哈希冲突,使得读写时间复杂度平均为O( ...
- 算法与数据结构基础 - 贪心(Greedy)
贪心基础 贪心(Greedy)常用于解决最优问题,以期通过某种策略获得一系列局部最优解.从而求得整体最优解. 贪心从局部最优角度考虑,只适用于具备无后效性的问题,即某个状态以前的过程不影响以后的状态. ...
- 算法与数据结构基础 - 数组(Array)
数组基础 数组是最基础的数据结构,特点是O(1)时间读取任意下标元素,经常应用于排序(Sort).双指针(Two Pointers).二分查找(Binary Search).动态规划(DP)等算法.顺 ...
- Python之数据结构基础
一.数据结构基础 a.什么是数据结构 b.数据结构的分类 c.列表 import random from timewrap import ...
- 数据结构基础(1)--数组C语言实现--动态内存分配
数据结构基础(1)--数组C语言实现--动态内存分配 基本思想:数组是最常用的数据结构,在内存中连续存储,可以静态初始化(int a[2]={1,2}),可以动态初始化 malloc(). 难点就是数 ...
- php数据结构课程---1、数据结构基础介绍(程序是什么)
php数据结构课程---1.数据结构基础介绍(程序是什么) 一.总结 一句话总结: 程序=数据结构+算法 设计好数据结构,程序就等于成功了一半. 数据结构是程序设计的基石. 1.数据的逻辑结构和物理结 ...
- 算法与数据结构基础 - 堆(Heap)和优先级队列(Priority queue)
堆基础 堆(Heap)是具有这样性质的数据结构:1/完全二叉树 2/所有节点的值大于等于(或小于等于)子节点的值: 图片来源:这里 堆可以用数组存储,插入.删除会触发节点shift_down.shif ...
- 算法与数据结构基础 - 广度优先搜索(BFS)
BFS基础 广度优先搜索(Breadth First Search)用于按离始节点距离.由近到远渐次访问图的节点,可视化BFS 通常使用队列(queue)结构模拟BFS过程,关于queue见:算法与数 ...
- 算法与数据结构基础 - 二叉树(Binary Tree)
二叉树基础 满足这样性质的树称为二叉树:空树或节点最多有两个子树,称为左子树.右子树, 左右子树节点同样最多有两个子树. 二叉树是递归定义的,因而常用递归/DFS的思想处理二叉树相关问题,例如Leet ...
随机推荐
- linux下内存大小、起始地址的解析与修改
在实际的工作中,由于产品型号的不同,经常需要调整linux所管理的内存的大小,而内核在启动阶段,会两次去解析从uboot传递过来的关于内存的信息,具体如下: 一.解析从uboot传递过来的tag(在p ...
- How Do I Declare A Block in Objective-C? [备忘]
How Do I Declare A Block in Objective-C? As a local variable: returnType (^blockName)(parameterTypes ...
- 阻塞IO服务器模型之多线程服务器模型
针对单线程服务器模型的特点,我们可以对其进行改进,使之能对多个客户端同时进行响应.最简单的改进即是使用多线程(或多进程)服务器模型,在应用层级别,我们一般采用多线程模式.多线程能让多个客户端同时请求, ...
- Android Multimedia框架总结(十一)CodeC部分之AwesomePlayer到OMX服务
转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52623882 前言:上篇文< ...
- 第一个lucene程序,把一个信息写入到索引库中、根据关键词把对象从索引库中提取出来、lucene读写过程分析
新建一个Java Project :LuceneTest 准备lucene的jar包,要加入的jar包至少有: 1)lucene-core-3.1.0.jar (核心包) 2) lucene- ...
- Android初级教程:ViewPage使用详解
转载本博客,请注明出处:http://blog.csdn.net/qq_32059827点击打开链接 ViewPage使用之一就是轮播广告,就以此为出发点,来详细解析一下ViewPage的使用和加载机 ...
- SSH网上商城---邮件发送
注册网站账号的时候,都需要发送激活邮件,然后让注册的用户点击激活链接方可完成注册,不过话说回来,为什么注册的时候需要发送邮件呢?为什么不注册的时候直接激活呢?一定要收一封激活帐号的邮件?网站这样做的好 ...
- Swift延迟加载的一种用途
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) 有以下一种情况: 我们试图用Cocoa的语音合成类NSSpee ...
- 4.QPixmap,QTransform,绘图函数的使用
新建一个项目Painter MyWidget.h #ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> class MyW ...
- 关于weak
#define DECLARE_WEAK_SELF __typeof(&*self) __weak weakSelf = self #define DECLARE_STRONG_SELF __ ...