数据结构(一)

数据结构是什么?

数据结构使指数据之间的关系,包含有逻辑结构和存储结构。
逻辑结构分为线性(一对一),树状(一对多),图状(多对多),集合(无明显对应)
存储要正确反应逻辑
常见的存储类型有vector,list,deque等等

数据结构初入门(排序算法)

推荐几个排序总结博客
各种排序算法总结
排序算法总结——时间复杂度与稳定性

关于排序稳定性的定义
通俗地讲就是能保证排序前两个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai = Aj,Ai原来在位置前,排序后Ai还是要在Aj位置前。

1.冒泡排序

先码上代码

#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<time.h>
#include<algorithm>
#include<cstring>
using namespace std;
void BubbleSort(int arr[], int length)
{
    clock_t start, end;
    start = clock();
    for (int i = 0; i < length; i++)
    {
        for (int j = 0; j < length - i - 1; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                swap(arr[j], arr[j + 1]);
            }
        }
    }
    end = clock();
    cout << end - start << "\n";
    for (int i = 0; i < length;i++)
        cout << arr[i] << " ";
}
int main()
{
    int n;
    cin >> n;
    int dta[100001];
    for (int i = 0; i < n;i++)
        cin >> dta[i];
    BubbleSort(dta, n);
}

bubblesort
冒泡算法这个名字就很形象
逐位对比 交换次序
第一次把最大的那个数“漂浮到”最后一位
第二次把次大的那个数“漂浮到”倒数第二位
……
示例
5 7 6 9(5和7 作比较)
5 7 6 9(7和6作比较)
5 6 7 9(7和9作比较 确定最后一位)
5 6 7 9(5和6作比较)
5 6 7 9(6和7作比较 确定第三位)
5 6 7 9(5和6作比较 确定第一位,第二位)
虽然在第三步的时候 就已经达到预期目标 但冒泡仍是要继续进行
既冒泡排序时间复杂度十分稳定
时间复杂度稳稳地O(n^2)

2.插入排序
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <time.h>
using namespace std;
void InsertSort(int arr[], int length)
{
    clock_t start, end;
    start = clock();
    for (int i = 0; i < length; i++)
    {
        for (int j = i; j > 0 && arr[j - 1] > arr[j]; j--)
        {
            swap(arr[j], arr[j - 1]);
        }
    }
    end = clock();
    cout << end - start << "\n";
    for (int i = 0; i < length; i++)
        cout << arr[i] << " ";
}
int main()
{
    int n;
    cin >> n;
    int dta[100001];
    for (int i = 0; i < n; i++)
        cin >> dta[i];
    InsertSort(dta, n);
}

在这里插入图片描述
这插入排序其实也较好理解
就是不断地向已排序完成序列中插入新的元素到合适的位置 使得形成长度加一的新的已排序完成序列 当然这个插入并不是找到位置放进去就可以的 是通过多个步骤的交换元素达到目的的
就拿序列5 7 6 9做例子
5 7 6 9(已排序完成序列:5 将要插入元素:7 将5和7 做比较 合法 停止交换)
5 7 6 9(已排序完成序列:5 7 将要插入元素:6 将6和7作比较 交换,将6和5作比较 合法 停止交换)
5 6 7 9(已排序完成序列:5 6 7 将要插入元素:9 将9和7作比较 合法 停止交换)

其实可以看到 插入排序的时间复杂度是不稳定的 最好的情况O(n)(已排好序情况) 最坏的情况O(n^2)(倒序)

3.快速排序
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <time.h>
using namespace std;
int Partion(int arr[], int left, int right)
{
    int x = arr[right];
    int i, j;

    for (i = j = left; i < right; i++)
    {
        if (arr[i] <= x)
        {
            swap(arr[i], arr[j++]);
        }
    }
    swap(arr[j], arr[right]);

    return j;
}
void QuickSort(int arr[], int left, int right)
{
    if (left < right)
    {
        int mid = Partion(arr, left, right);
        QuickSort(arr, left, mid - 1);
        QuickSort(arr, mid + 1, right);
    }
}
void QuickSort(int arr[], int length)
{
    clock_t start, end;
    start = clock();
    QuickSort(arr, 0, length - 1);
    end = clock();
    cout << end - start << "\n";
    for (int i = 0; i < length; i++)
        cout << arr[i] << " ";
}
int main()
{
    int n;
    cin >> n;
    int dta[100001];
    for (int i = 0; i < n; i++)
        cin >> dta[i];
    QuickSort(dta, n);
}

在这里插入图片描述
快速排序中用到了分治的思想,将一个数组分成两个子数组,对两部分进行独立地排序。当两个子数组有序时整个数组也就自然有序了。快速排序中,递归调用发生在处理整个数组之后,切分的位置取决于数组的内容。
该方法的基本思想是:
1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。

4.希尔排序
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <time.h>
using namespace std;
void ShellSort(int arr[], int length)
{
    clock_t start, end;
    start = clock();
    for (int inc = length / 2; inc > 0; inc /= 2)
    {
        for (int i = inc; i < length; i++)
        {
            for (int j = i; j >= inc && arr[j - inc] > arr[j]; j -= inc)
            {
                swap(arr[j], arr[j - inc]);
            }
        }
    }
    end = clock();
    cout << end - start << "\n";
    for (int i = 0; i < length; i++)
        cout << arr[i] << " ";
}
int main()
{
    int n;
    cin >> n;
    int dta[100001];
    for (int i = 0; i < n; i++)
        cin >> dta[i];
    ShellSort(dta, n);
}

维基百科---shellsort
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

希尔排序(Shellsort),也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
例如,假设有这样一组数[ 13 14 94 33 82 25 59 94 65 23 45 27 73 25 39 10 ],如果我们以步长为5开始进行排序,我们可以通过将这列表放在有5列的表中来更好地描述算法,这样他们就应该看起来是这样:
13 14 94 33 82
25 59 94 65 23
45 27 73 25 39
10
然后我们对每列进行排序:
10 14 73 25 23
13 27 94 33 39
25 59 94 65 82
45
将上述四行数字,依序接在一起时我们得到:[ 10 14 73 25 23 13 27 94 33 39 25 59 94 65 82 45 ].这时10已经移至正确位置了,然后再以3为步长进行排序:
10 14 73
25 23 13
27 94 33
39 25 59
94 65 82
45
排序之后变为:
10 14 13
25 23 33
27 25 59
39 65 73
45 94 82
94
最后以1步长进行排序(此时就是简单的插入排序了)。
来源:维基百科

5.堆排序

维基百科--heapsort
堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。

若以升序排序说明,把数组转换成最大堆积(Max-Heap Heap),这是一种满足最大堆积性质(Max-Heap Property)的二叉树:对于除了根之外的每个节点i, A[parent(i)] ≥ A[i]。

重复从最大堆积取出数值最大的结点(把根结点和最后一个结点交换,把交换后的最后一个结点移出堆),并让残余的堆积维持最大堆积性质。

堆节点的访问
通常堆是通过一维数组来实现的。在数组起始位置为0的情形中:
父节点i的左子节点在位置 (2i+1);
父节点i的右子节点在位置 (2i+2);
子节点i的父节点在位置floor((i-1)/2);

堆的操作
在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:

最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
创建最大堆(Build Max Heap):将堆中的所有数据重新排序
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

5.归并排序
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <time.h>
using namespace std;
void Merge(int arr[], int aux[], int left, int mid, int right)
{
    int i = left;
    int j = mid + 1;
    int k = left;

    while (i <= mid && j <= right)
    {
        if (arr[i] > arr[j])
        {
            aux[k++] = arr[j++];
        }
        else
        {
            aux[k++] = arr[i++];
        }
    }
    while (i <= mid)
    {
        aux[k++] = arr[i++];
    }
    while (j <= right)
    {
        aux[k++] = arr[j++];
    }
    for (int i = left; i <= right; i++)
    {
        arr[i] = aux[i];
    }
}
void read(int &x)
{
    int f = 1;
    x = 0;
    char s = getchar();
    while (s < '0' || s > '9')
    {
        if (s == '-')
            f = -1;
        s = getchar();
    }
    while (s >= '0' && s <= '9')
    {
        x = x * 10 + s - '0';
        s = getchar();
    }
    x *= f;
}
void MergeSort(int arr[], int aux[], int left, int right)
{
    if (left < right)
    {
        int mid = left + (right - left) / 2;
        MergeSort(arr, aux, left, mid);
        MergeSort(arr, aux, mid + 1, right);
        Merge(arr, aux, left, mid, right);
    }
}
void MergeSort(int arr[], int length)
{
    clock_t start, end;
    start = clock();
    int *aux = new int[length];
    MergeSort(arr, aux, 0, length - 1);
    delete[] aux;
    end = clock();
    cout << end - start << "\n";
    // for (int i = 0; i < length; i++)
    //     printf("%d ", arr[i]);
}
int dta[498844223];
int main()
{
    int n;
    read(n);

    for (int i = 0; i < n; i++)
        read(dta[i]);
    MergeSort(dta, n);
}

维基百科---mergesort
采用分治法:
分割:递归地把当前序列平均分割成两半。
集成:在保持元素顺序的同时将上一步得到的子序列集成到一起(归并)。
归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作。归并排序算法依赖归并操作。

递归法(Top-down)
申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
设定两个指针,最初位置分别为两个已经排序序列的起始位置
比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针到达序列尾
将另一序列剩下的所有元素直接复制到合并序列尾

迭代法(Bottom-up)
原理如下(假设序列共有n个元素):
将序列每相邻两个数字进行归并操作,形成{\displaystyle ceil(n/2)}个序列,排序后每个序列包含两/一个元素
若此时序列数不是1个则将上述序列再次归并,形成{\displaystyle ceil(n/4)}个序列,每个序列包含四/三个元素
重复步骤2,直到所有元素排序完毕,即序列数为1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值