分治策略
二分查找
循环二分查找
int BinaryFindValue(const int* ar, int n, int val)//循环的二分查询 { assert(ar != NULL); // NULL nullptr; int pos = -1; int left = 0, right = n - 1; while (left <= right) // left < right { //int mid = (right + left) / 2;//可能左右相加超出整形数组的范围, int mid = (right - left) / 2 + left;// if (val < ar[mid]) { right = mid - 1; } else if (val > ar[mid]) { left = mid + 1; } else { while (mid > left && ar[mid - 1] == val) { mid = mid - 1; } pos = mid; break; } } return pos; } int main() { int ar[] = { 12,23,34,45,56,67,78,89,90,100 }; int n = sizeof(ar) / sizeof(ar[0]); int val; cin >> val; int pos = BinaryFindValue(ar, n, val);//二分查询 cout << pos << endl; return 0; }
递归二分查找
int FindValue(const int* ar, int left, int right, int val) { int pos = -1; if (left <= right) { int mid = (right - left) / 2 + left; if (val < ar[mid]) { pos = FindValue(ar, left, mid - 1, val); } else if (val > ar[mid]) { pos = FindValue(ar, mid + 1, right, val); } else { pos = mid; } } return pos; } //递归的二分查询 int BinFindValue(const int* ar, int n, int val) { assert(ar != nullptr); return FindValue(ar, 0, n - 1, val); } int main() { int ar[] = { 12,23,34,45,56,67,78,89,90,100 }; int n = sizeof(ar) / sizeof(ar[0]); int val; cin >> val; pos = BinFindValue(ar, n, val);//递归的二分查询 cout << pos << endl; return 0; }
快速排序法
递归
void Print_Ar(int* ar, int n) { for (int i = 0; i < n; ++i) { printf("%4d ", ar[i]); } printf("\n"); } int Parition(int* ar, int left, int right) { int tmp = ar[left]; while (left < right) { while (left < right && tmp < ar[right]) --right; if (left < right) ar[left] = ar[right]; while (left < right && ar[left] <= tmp) ++left; if (left < right) ar[right] = ar[left]; } ar[left] = tmp; return left; } void QuickPass(int* ar, int left, int right)//快速排序 { if (left < right) { int pos = OneParition(ar, left, right); QuickPass(ar, left, pos - 1); QuickPass(ar, pos + 1, right); } } void QuickSort(int* ar, int n)//快排 { assert(ar != NULL); QuickPass(ar, 0, n - 1); } int main() { int ar[] = { 56,23,34,89,90,12,78,45,56,100,67 }; int n = sizeof(ar) / sizeof(ar[0]); Print_Ar(ar, n); QuickSort(ar, n); Print_Ar(ar, n); return 0; }
栈、
void NiceQuickSort(int* ar, int n) { assert(ar != nullptr); stack<int> ist; ist.push(0); ist.push(n - 1); while (!ist.empty()) { int right = ist.top(); ist.pop(); int left = ist.top(); ist.pop(); int pos = Parition(ar, left, right); if (left < pos - 1) { ist.push(left); ist.push(pos - 1); } if (pos + 1 < right) { ist.push(pos + 1); ist.push(right); } } }
队列
void NiceQuickSort(int* ar, int n) { assert(ar != nullptr); queue<int> ist; ist.push(0); ist.push(n - 1); while (!ist.empty()) { int left = ist.front(); ist.pop(); int right = ist.front(); ist.pop(); int pos = Parition(ar, left, right); if (left < pos - 1) { ist.push(left); ist.push(pos - 1); } if (pos + 1 < right) { ist.push(pos + 1); ist.push(right); } } }
队的另一种方法:
void NiceQuickSort(int* ar, int n) { assert(ar != nullptr); queue<std::pair<int, int> > ist;//pair 一对 有first,second std::pair<int, int> LR(0, n - 1); ist.push(LR); while (!ist.empty()) { std::pair<int, int> LR = ist.front(); ist.pop(); int pos = RandParition(ar, LR.first, LR.second); if (LR.first < pos - 1) { ist.push(pair<int, int>(LR.first, pos - 1)); } if (pos + 1 < LR.second) { ist.push(pair<int, int>(pos + 1, LR.second)); } } }
若数据本来有序,(若从第一个开始划分,变成冒泡排序)
使用随机法
int Parition(int* ar, int left, int right) { int tmp = ar[left]; while (left < right) { while (left < right && tmp < ar[right]) --right; if (left < right) ar[left] = ar[right]; while (left < right && ar[left] <= tmp) ++left; if (left < right) ar[right] = ar[left]; } ar[left] = tmp; return left; } int RandParition(int* ar, int left, int right) { assert(ar != nullptr); int index = rand() % (right - left + 1) + left; //模值加left,模值得到的是第几位,而不是物理下标,加left得到那一位的物理下标 std::swap(ar[index], ar[left]); return Parition(ar, left, right); }
适合单链表的:冒泡,选择
从一头进行逼近
int OneParition(int* ar, int left, int right) { int i = left, j = left - 1; int tmp = ar[i]; while (i <= right) { if (ar[i] <= tmp) { j = j + 1; std::swap(ar[i], ar[j]); } ++i; } std::swap(ar[left], ar[j]); return j; }
单链表的快排
含头结点的快排:
typedef int ElemType; typedef struct ListNode { ElemType data; struct ListNode* next; }ListNode, * LinkList; struct ListNode* Buynode() { struct ListNode* s = (struct ListNode*)malloc(sizeof(struct ListNode)); if (NULL == s) exit(1); memset(s, 0, sizeof(struct ListNode)); return s; } struct ListNode* InitList() { struct ListNode* s = Buynode(); return s; } void Push_Front(LinkList phead, ElemType val) { ListNode* s = Buynode(); s->data = val; s->next = phead->next; phead->next = s; } void PrintList(LinkList phead) { ListNode* p = phead->next; while (p != nullptr) { printf("%d ", p->data); p = p->next; } printf("\n"); } ListNode* Parition(ListNode* first, ListNode* end) { ListNode* jp = first; ListNode* ip = first->next;//ip指向第一个数据的结点 int tmp = ip->data; while (ip != end) { if (ip->data <= tmp) { jp = jp->next; std::swap(jp->data, ip->data);//交换函数 } ip = ip->next; } std::swap(first->data, jp->data);//退出后第一个与jp指向的交换 return jp; } void QuickPass(ListNode* first, ListNode* end) { if (first != end)//first指第一个结点,end指向的是空 { ListNode* pos = Parition(first, end); QuickPass(first, pos); QuickPass(pos, end); } } void QuickSort(ListNode head) { assert(head != nullptr); QuickPass(head, NULL); } int main() { int ar[] = { 56,23,34,89,90,12,78,45,100,67 }; int n = sizeof(ar) / sizeof(ar[0]); LinkList head = InitList(); for (int i = n - 1; i >= 0; --i) { Push_Front(head, ar[i]); } PrintList(head); QuickSort(head); PrintList(head); return 0; }
不含头结点的快排:
typedef int ElemType; typedef struct ListNode { ElemType data; struct ListNode* next; }ListNode, * LinkList; struct ListNode* Buynode() { struct ListNode* s = (struct ListNode*)malloc(sizeof(struct ListNode)); if (NULL == s) exit(1); memset(s, 0, sizeof(struct ListNode)); return s; } struct ListNode* InitList() { struct ListNode* s = Buynode(); return s; } ListNode* Push_Front(LinkList phead, ElemType val) { if (phead == nullptr) { ListNode* s = Buynode(); s->data = val; s->next = nullptr; return s; } ListNode* s = Buynode(); s->data = val; s->next = phead; phead = s; return phead; } void PrintList(LinkList phead) { ListNode* p = phead; while (p != nullptr) { printf("%d ", p->data); p = p->next; } printf("\n"); } ListNode* Parition(ListNode* first, ListNode* end) { ListNode* jp = first; ListNode* ip = first->next;//ip指向第一个数据的结点 int tmp = jp->data; while (ip != end) { if (ip->data <= tmp) { jp = jp->next; std::swap(jp->data, ip->data);//交换函数 } ip = ip->next; } std::swap(first->data, jp->data);//退出后第一个与jp指向的交换 return jp; } void QuickPass(ListNode* first, ListNode* end) { if (first != end)//first指第一个结点,end指向的是空 { ListNode* pos = Parition(first, end); QuickPass(first, pos); QuickPass(pos->next, end); } } void QuickSort(ListNode* phead) { assert(phead != nullptr); QuickPass(phead, NULL); } int main() { int ar[] = { 56,23,34,89,90,12,78,45,100,67 }; int n = sizeof(ar) / sizeof(ar[0]); LinkList head = nullptr; for (int i = n - 1; i >= 0; --i) { head = Push_Front(head, ar[i]); } PrintList(head); QuickSort(head); PrintList(head); return 0; }
寻找第k小的值:
int Parition(int* ar, int left, int right) { int tmp = ar[left]; while (left < right) { while (left < right && tmp < ar[right]) --right; if (left < right) ar[left] = ar[right]; while (left < right && ar[left] <= tmp) ++left; if (left < right) ar[right] = ar[left]; } ar[left] = tmp; return left; } int SelectK(int* ar, int left, int right, int k) { if (left == right && k == 1) return ar[left];//此时下标只有一个值 int pos = Parition(ar, left, right); int j = pos - left + 1; if (k <= j) return SelectK(ar, left, pos, k); else return SelectK(ar, pos + 1, right, k - j); } int Select_K_Min(int* ar, int n, int k)//找第k小 { assert(ar != NULL && k >= 1 && k <= n); return SelectK(ar, 0, n - 1, k); } int main() { int ar[] = { 56,23,34,89,90,12,78,45,100,67 }; int n = sizeof(ar) / sizeof(ar[0]); for (int k = 1; k <= n; ++k) { printf("%d => %d \n", k, Select_K_Min(ar, n, k)); } return 0; }
分治算法
怎样找哪两个值之间差值最小
int Parition(int* ar, int left, int right) { int tmp = ar[left]; while (left < right) { while (left < right && tmp < ar[right]) --right; if (left < right) ar[left] = ar[right]; while (left < right && ar[left] <= tmp) ++left; if (left < right) ar[right] = ar[left]; } ar[left] = tmp; return left; } int SelectK(int* ar, int left, int right, int k) { if (left == right && k == 1) return ar[left];//此时下标只有一个值 int pos = Parition(ar, left, right); int j = pos - left + 1;//pos-left指物理下标,实第几小时应加1 if (k <= j) return SelectK(ar, left, pos, k); else return SelectK(ar, pos + 1, right, k - j); } int Select_K_Min(int* ar, int n, int k)//找第k小 { assert(ar != NULL && k >= 1 && k <= n); return SelectK(ar, 0, n - 1, k); } int MaxS1(int* ar, int left, int right) { return ar[right]; } int MinS2(int* ar, int left, int right)//物理区域找最小值 { int min = ar[left]; for (int i = left + 1; i <= right; ++i) { if (min > ar[i]) { min = ar[i]; } } return min; } int Min(int a, int b) { return a < b ? a : b; } int Min3(int a, int b, int c)//三个数比较最小值 { return Min(Min(a, b), c); } int Capir(int* ar, int left, int right) {if (right - left <= 1) { return INT_MAX; } int k = (right - left + 1) / 2; SelectK(ar, left, right, k); int pos = k + left - 1;//物理下标 //此时是物理下标 int d1 = Capir(ar, left, pos); //s1; int d2 = Capir(ar, pos + 1, right); //s2; int p = MaxS1(ar, left, pos); //s1; int q = MinS2(ar, pos + 1, right);return Min3(d1, d2, d3); } int main() { int ar[] = { 56,23,34,89,92,12,78,45,100,67,55 }; int n = sizeof(ar) / sizeof(ar[0]); int dist= Capir(ar, 0, n - 1); printf("%d \n",dist); return 0; }
归并函数
递归方法
在递归过程进行不断分割,当分割到最小值时,对最小值进行并归
递归时分割,回归时排序
void Copy(int* ar, int* br, int left, int right)//拷贝 { for (int i = left; i <= right; ++i) { ar[i] = br[i]; } } void Merge(int* br, int* ar, int left, int m, int right)//归并 { assert(br != nullptr && ar != nullptr); int i = left, j = m + 1; int k = left; while (i <= m && j <= right) { br[k++] = ar[i] <= ar[j] ? ar[i++] : ar[j++]; } while (i <= m) { br[k++] = ar[i++]; } while (j <= right) { br[k++] = ar[j++]; } } void PassMerge(int* br, int* ar, int left, int right) { if (left < right) { int mid = (right - left) / 2 + left; PassMerge(br, ar, left, mid); PassMerge(br, ar, mid + 1, right); Merge(br, ar, left, mid, right);//并归到ar Copy(ar, br, left, right);//从ar拷贝到br } } void MergeSort(int* ar, int n) { assert(ar != nullptr); int* br = new int[n]; // int *br = (int*)malloc(sizeof(int)*n); PassMerge(br, ar, 0, n - 1);//并归 delete[]br; }int main() { int ar[] = { 34,45,12,78,90,100,23,56,67,89 }; int n = sizeof(ar) / sizeof(ar[0]); NiceMergeSort(ar, n); return 0; }
非递归方法
s代表窗体的大小
void Copy(int* ar, int* br, int left, int right)//拷贝 { for (int i = left; i <= right; ++i) { ar[i] = br[i]; } } void Merge(int* br, int* ar, int left, int m, int right)//归并 { assert(br != nullptr && ar != nullptr); int i = left, j = m + 1; int k = left; while (i <= m && j <= right) { br[k++] = ar[i] <= ar[j] ? ar[i++] : ar[j++]; } while (i <= m) { br[k++] = ar[i++]; } while (j <= right) { br[k++] = ar[j++]; } } void PassMerge(int* br, int* ar, int left, int right) { if (left < right) { int mid = (right - left) / 2 + left; PassMerge(br, ar, left, mid); PassMerge(br, ar, mid + 1, right); Merge(br, ar, left, mid, right);//并归到ar Copy(ar, br, left, right);//从ar拷贝到br } } void MergeSort(int* ar, int n) { assert(ar != nullptr); int* br = new int[n]; // int *br = (int*)malloc(sizeof(int)*n); PassMerge(br, ar, 0, n - 1);//并归 delete[]br; } void NicePass(int* dest, int* src, int n, int s) { // s= 4 假设每个窗口大小为4 int i = 0; for (; i + 2 * s - 1 <= n - 1; i += 2 * s) { Merge(dest, src, i, i + s - 1, i + 2 * s - 1);//i+s-1这个窗体的最后的下标,第二窗体最后的下标 } if (n - 1 >= i + s)//当总数大于i+s时 { Merge(dest, src, i, i + s - 1, n - 1);//n-1最后一个元素 } else { for (int j = i; j < n; ++j)//剩余并归不参与的数据,直接拷贝 { dest[j] = src[j]; } } } void NiceMergeSort(int* ar, int n) { assert(ar != nullptr); int* br = new int[n]; int s = 1;//s代表区域的元素的个数 while (s < n) { NicePass(br, ar, n, s); s += s; // 2 NicePass(ar, br, n, s); s += s;// 4 } delete[]br; } int main() { int ar[] = { 34,45,12,78,90,100,23,56,67,89 }; int n = sizeof(ar) / sizeof(ar[0]); NiceMergeSort(ar, n); return 0; }
堆排
大端小端
左孩子2*i+1,右孩子2*i+2
堆排序的时间复杂度比直接排序低
孩子结点为j,双亲的结点为(j-1)/2
downfilter
upfliter
using namespace std; //堆排 template<class Type> class MinHeap { vector<Type> data; void UpFilter(int begin)//向上筛选 { int j = begin;//指的是最后一个下标 int i = (j - 1) / 2;//指向的是它的双亲结点 Type tmp = data[j]; while (j > 0) { if (data[i] <= tmp) break; data[j] = data[i]; j = i; i = (j - 1) / 2; } data[j] = tmp; } void DownFilter(int begin, int end)//向下筛选 { int i = begin; int j = i * 2 + 1; // left孩子; Type tmp = data[i];//取data中的值 while (j <= end) // { if (j < end && data[j] > data[j + 1]) j = j + 1; if (tmp <= data[j]) break; data[i] = data[j]; i = j; j = j * 2 + 1; } data[i] = tmp; } public: MinHeap() {} MinHeap(int* ar, int n) { assert(ar != nullptr); for (int i = 0; i < n; ++i) { data.push_back(ar[i]); } int end = n - 1; int pos = (end - 1) / 2;//第一个分治点 while (pos >= 0) { DownFilter(pos, end); --pos; } } ~MinHeap() {} bool IsEmpty() const { return data.empty(); } size_t Size() const//元素个数 { return data.size(); } void Push(const Type& x) { data.push_back(x);//尾部入 UpFilter(data.size() - 1);//进行向上调整 } Type& Get() //取出一个元素 { return data.front(); } const Type& Get() const { return data.front(); } void Pop()//删除首元素 { data[0] = data[data.size() - 1]; data.pop_back(); if (!IsEmpty()) { DownFilter(0, data.size() - 1); } } void Sort()//堆排序 { int pos = data.size() - 1; while (pos > 0) { std::swap(data[0], data[pos]); DownFilter(0, pos - 1); pos = pos - 1; } } }; int main() { int ar[] = { 45,56,78,12,34,90,100,78,89,23,67 }; int n = sizeof(ar) / sizeof(ar[0]); MinHeap<int> hm(ar, n); hm.Sort(); return 0; } #if 0 int main()//最小堆的测试 { MinHeap<int> hm; hm.Push(23); hm.Push(12); hm.Push(90); hm.Push(34); hm.Push(67); cout << hm.Get() << endl; hm.Pop(); hm.Push(8); hm.Push(78); cout << hm.Get() << endl; hm.Pop(); cout << hm.Get() << endl; hm.Pop(); cout << hm.Get() << endl; hm.Pop(); }
斐波拉数列
1 1 2 3 5 8 13 21 34 55
1 2 3 4 5 6 7 8 9 10
通过循环计算斐波拉数列
时间复杂度:o(n)
int fun(int n) { int a = 1, b = 1, c = 1; for (int i = 3; i <= n; ++i) { c = a + b; b = a; a = c; } return c; }
通过递归
时间复杂度:o(2的n次方)
空间复杂度:s(n) (数的高度)
int fac(int n) { if (n == 1 || n == 2) return 1; else return fac(n - 1) + fac(n - 2); }
时间复杂度为o(n)的递归:
int facc(int n, int a, int b) { if (n == 1 || n == 2) return a; else return facc(n - 1, a + b, a); } int fac(int n) { int a = 1, b = 1; return facc(n, a, b); }
排列问题
全排列
void Perm(int* ar, int k, int m) { if (k == m)//区域中有一个值 { for (int i = 0; i <= m; ++i)//打印函数 { printf("%3d ", ar[i]); } printf("\n"); } else { for (int j = k; j <= m; ++j) { std::swap(ar[j], ar[k]);//交换 Perm(ar, k + 1, m);//进行全排列 std::swap(ar[j], ar[k]);//交换回来 } } } int main() { int ar[] = { 1,2,3 }; int n = sizeof(ar) / sizeof(ar[0]); Perm(ar, 0, n - 1); return 0; }
整数的划分
int q(int n, int m)//m为最大加数 { if (n < 1 || m < 1) return 0; if (n == 1 || m == 1) return 1; if (n < m) return q(n, n); if (n == m) return 1 + q(n, n - 1);//公式,直接记住 else return q(n, m - 1) + q(n - m, m);//公式 } int main() { int n = 6, m = 5; // n = n1 + n2 + n3 + ..+ nk printf("%d \n", q(n, m)); return 0; }
子集问题(回溯)
void fun(int* ar, int* br, int i, int n) { if (i == n) { for (int j = 0; j < n; ++j) { if (br[j] == 1)//br定义为000,当为1时,直接打印123 { printf("%2d ", ar[j]); } } printf("\n"); } else { br[i] = 1;//往左边跑,数值为1 fun(ar, br, i + 1, n); br[i] = 0;//往右边跑。数值为0 fun(ar, br, i + 1, n); } } int main() { int ar[] = { 1,2,3 }; int br[] = { 0,0,0 }; fun(ar, br, 0, 3); return 0; }