【算法与数据结构】LeetCode介绍、简单排序算法、数组(笔记一)


  最近博主学习了算法与数据结构的一些视频,在这个文章做一些笔记和心得,本篇文章就写了一些基础算法和数据结构的知识点,具体题目解析会放在另外一篇文章。在学习时已经有C, C++的基础。文章附上了学习的代码,仅供大家参考。如果有问题,有错误欢迎大家留言。算法与数据结构一共有三篇文章,剩余文章可以在 【CSDN文章】晚安66博客文章索引找到。

一、算法和数据结构和LeetCode介绍

  当年美国登月时用的算法程序大小不到1M,科学家用他们的算法和数据结构知识将程序缩减到极致,也就是说,KB级别的程序就可以登月了,哈哈!目前很多大厂都要考算法和数据结构,对于在岗的程序员来说,哪怕你现学现卖,面向百度编程、面向Google编程,一些可以查网络,查书的知识反而不是那么重要,而算法和数据结构就比较重要了,以Google搜索引擎来说,每天访问的次数是上亿次,算法上哪怕改进0.001%都能给Google公司省下大量的费用。算法和数据结构需要长期的训练,需要培养算法思维,或者说需要一点悟性。

  力扣(LeetCode)题库为力扣用户进行题目练习的主要入口,支持C++、Java、Python、Rust、Kotlin等十多种编程语言,已上线超过1000道原创编程题,涉及包括贪心、动态规划、链表、二叉树、哈希表等知识点的算法与数据结构,并按难度分为简单、中等、困难三个等级。

  那么我建议大家按照下面这个顺序开始刷,难度就从简单刷起,做了几个类型题目之后,再慢慢做中等题目、困难题目。

数组-> 链表-> 哈希表->字符串->栈与队列->树->回溯->贪心->动态规划->图论->高级数据结构.

二、算法和数据结构入门

2.1 时间复杂度

  一个操作如果和样本的数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作。时间复杂度为一个算法流程中,常数操作数量的一个指标。常用O(读作big O)来表示。大O用来表示上界的,当用它作为算法的最坏情况运行时间的上界,就是对任意数据输入的运行时间的上界。具体来说,先要对一个算法流程非常熟悉,然后去写出这个算法流程中,发生了多少常数操作,进而总结出常数操作数量的表达式。在表达式中,只要高阶项,不要低阶项,也不要高阶项的系数(当 N N N大到一定程度以后,高阶项占据主导,系数也无关紧要),剩下的部分如果为 f ( N ) f(N) f(N),那么时间复杂度为 O ( f ( N ) ) O(f(N)) O(f(N))评价一个算法流程的好坏,先看时间复杂度的指标,然后再分析不同数据样本下的实际运行时间,也就是“常数项时间”。

  例如冒泡排序算法,我们需要找对比 N ∗ ( N − 1 ) 2 \frac {N *(N-1)}{2} 2N(N1)次,即进行 N ∗ ( N − 1 ) 2 \frac {N *(N-1)}{2} 2N(N1)次常数操作,因此时间复杂度为 O ( N 2 ) O(N^2) O(N2)。如果时间复杂度一致,那么就要比较常数操作的实际运行时间,也就是要实际跑代码,对比时间长短,得到算法优劣。

2.2 空间复杂度

  空间复杂度(Space Complexity)是对算法运行过程中临时占用空间大小的度量,记作 S ( n ) S(n) S(n)。算法执行所需要的临时空间不随着某个变量N的大小而变化,即此算法空间复杂度为一个常量, 可表示为 O ( 1 ) O(1) O(1)
  那么具体什么代码的空间复杂度为 O ( 1 ) O(1) O(1)呢?

int j = 0;
for (int i = 0; i < n; i++) {
    j++;
}

  第一段代码我们可以看出,随着n的变化,所需开辟的内存空间并不会随着n的变化而变化,即此算法空间复杂度为一个常量,所以表示为大 O(1)。
  那么什么代码的空间复杂度又为 O ( N ) O(N) O(N)呢?

int* a = new int(n);
for (int i = 0; i < n; i++) {
    a[i] = i;
} 

  我们new了一个数组出来,这个数据占用的大小为 N N N虽然有一个for循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可随着 N N N的增大,开辟的内存大小呈线性增长,即 O ( N ) O(N) O(N),其他的 O ( n 2 ) , O ( n 3 ) O(n^2), O(n^3) O(n2)O(n3),我想大家应该都可以以此例举出来了。特别要提到的是一般递归的算法需要的复杂度可能为 O ( l o g ( N ) ) O(log(N)) O(log(N)),可以发现,我们这里的对数是忽略底数的,因为根据对数的换底公式,不同底数之间的的对数只差一个常数。

  对于一个算法来说,它的时间复杂度和空间复杂度往往是相互影响的。那我们熟悉的 Chrome 来说,流畅性方面比其他厂商好了多人,但是占用的内存空间略大。当追求一个较好的时间复杂度时,可能需要消耗更多的储存空间。 反之,如果追求较好的空间复杂 度,算法执行的时间可能就会变长。

  常见的复杂度不多,从低到高排列就这么几个: O ( 1 ) O(1) O(1) O ( l o g ( N ) ) O(log(N)) O(log(N)) O ( N ) O(N) O(N) O ( N 2 ) O(N^2) O(N2)。 除了时间复杂度和空间复杂度的区分以外,还有最好、最坏、平均、均摊时间复杂度的区别。

2.3 基础排序算法

2.3.1 选择排序算法

  先随便选一个元素假设它为最小的元素(默认为序列的第一个元素),然后让这个元素与序列中的每一个元素进行比较,如果遇到比自己小的元素,那更新最小值下标,直到把序列中的元素遍历完,那最后的最小值就是序列中的最小值。

例如: 使用选择排序算法将数组 { 4,2,8,0,5,7,1,3,6,9 } 进行升序排序。
   
步骤:

  • 在一个长度为n的无序数组中,第一次遍历n-1个数找到最小的和第一个数交换。
  • 第二次从下一个数开始遍历n-2个数,找到最小的数和第二个数交换。
  • 重复以上操作直到第n-1次遍历最小的数和第n-1个数交换,排序完成。

时间复杂度:
  对于长度为 N N N的数组,代码执行的时间都花费在内层for循环中的比较语句和外层for循环里的交换语句了,因此时间复杂度为 O ( N 2 ) O(N^2) O(N2)

稳定性:
  由于选择元素之后会发生交换操作,有可能把前面的元素交换到后面,所以选择排序不是稳定的排序。

// 选择排序算法
void SelectSort(int* list, const int n)
{
	for (int i = 0; i < n - 1; i++){
		int min = i;  //min为最小值索引
		for (int j = i + 1; j < n; j++){
			if (list[j] < list[min]){
				min = j;
			}
		}
		swap(list[i], list[min]);
	}
}

2.3.2 冒泡排序算法

三、数组

  数组是存放在连续内存空间上的相同类型数据的集合。数组具有下面的特点:

  • 数组下标从0开始。
  • 数组内存空间的地址都是连续的。

  正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。也就是说,数组不能释放单一元素,如果要释放,就是全释放(程序运行结束,回首内存栈空间)。不同编程语言当中,二维数组的内存管理不一样,例如C++中二维数组是连续内存分布,Java不是连续内存分布。

3.1 二分法查找法

  二分法要求的数组必须是有序数组。二分法需要注意的就是边界条件以及while循环什么时候停止。边界条件是[left,right)还是[left,right]决定了while循环当中的停止条件是left<right还是left<=right。确定万边界条件后就变得简单了。
程序如下:

	int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int middle;
        while (left <= right) {
            middle = (left + right) / 2;
            if (nums[middle] < target) {
                left = middle + 1;               
            } else if (nums[middle] > target){
                right = middle - 1;
            } else {
                return middle;
            }
        }
        return -1;
	}

复杂度分析:

  • 时间复杂度: O ( l o g n ) O(log n) O(logn),二分法查找的查找速度是 l o g 2 n log_{2}{n} log2n,可以去掉底数(乘上一个常数),结果就是 O ( l o g n ) O(log n) O(logn)
  • 空间复杂度: O ( 1 ) O(1) O(1), 开辟的内存空间大小是一个常量。

3.2 双指针法

  双指针法也能叫做快慢指针法:通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

  • 暴力解法时间复杂度: O ( n 2 ) O(n^2) O(n2),两个for循环。
  • 双指针时间复杂度: O ( n ) O(n) O(n),一个for循环。

end

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晚安66

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值