分治策略

二分查找

循环二分查找

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;
}

 

posted @ 2021-08-18 14:03  阿拉佩丫  阅读(49)  评论(0)    收藏  举报