算法总结一LeetCode

总结一下这两个来月写的算法,都是LeetCode上的。

题目类型

LeetCode71     LeetCode155

字符串

LeetCode14     LeetCode6     LeetCode242     LeetCode520     LeetCode58     LeetCode696

分治

LeetCode169     LeetCode53

哈希

LeetCode169     LeetCode1     LeetCode242     LeetCode217     LeetCode219     LeetCode347     LeetCode387

排序

LeetCode169     LeetCode

深度广度优先

LeetCode200     LeetCode127     LeetCode130

二分搜索

LeetCode35     LeetCode34

拓扑排序

LeetCode207     LeetCode210

优先队列 堆

LeetCode767     LeetCode347

排序

LeetCode767     LeetCode769     LeetCode57      LeetCode75

动态规划

LeetCode5     LeetCode96     LeetCode53     LeetCode70     LeetCode198

数组

LeetCode1     LeetCode456     LeetCode88     LeetCode56

回溯

LeetCode46     LeetCode78     LeetCode22     LeetCode47     LeetCode51

LeetCode100     LeetCode111     LeetCode199     LeetCode257     LeetCode236

并查集

LeetCode684

链表

LeetCode83     LeetCode2

数学

LeetCode204     LeetCode66

贪心

LeetCode455


各类型题目总结

栈:

栈的特点先进后出,数据结构中用栈比如匹配括号,数值转换,迷宫求解,表达式求解(这个还有就是什么中缀表达式和前缀表达式的转换,之后再进行表达式求解)等等的吧。

在java中,stack.pop()取出并返回栈顶的值,stack.peek()只返回栈顶值并不取出,stack.push()元素入栈,在java里的Stack类里还有一个add方法,也可以入栈,只不过add是继承与Vector来的方法,而push方法是Stack类扩展出来的。

字符串:

字符串的题目写了很多啊,有的就很简单,简单处理一下就可以了,大部分还是需要观察找到一定的规律去解决。

想说一下KMP算法,改进的字符串匹配算法,这个每次写,每次迷,主要是里面的next数组,找字符串的前缀和后缀最长相同部分。我就从网上找个代码粘过来了。。嘻嘻。。网上也有一大堆一大堆的介绍KMP的,都很详细的。

void GetNextval(char* p, int next[])//next数组求法
{
	int pLen = strlen(p);
	next[0] = -1;
	int k = -1;
	int j = 0;
	while (j < pLen - 1)
	{
		//p[k]表示前缀,p[j]表示后缀  
		if (k == -1 || p[j] == p[k])
		{
			++j;
			++k;
			if (p[j] != p[k])
				next[j] = k;  
			else
				//因为不能出现p[j] = p[ next[j ]],所以当出现时需要继续递归,k = next[k] = next[next[k]]
				next[j] = next[k];
		}
		else
		{
			k = next[k];
		}
	}
}
int KmpSearch(char* s, char* p)//KMP算法
{
	int i = 0;
	int j = 0;
	int sLen = strlen(s);
	int pLen = strlen(p);
	while (i < sLen && j < pLen)
	{
		//①如果j = -1,或者当前字符匹配成功(即S[i] == P[j]),都令i++,j++    
		if (j == -1 || s[i] == p[j])
		{
			i++;
			j++;
		}
		else
		{
			//②如果j != -1,且当前字符匹配失败(即S[i] != P[j]),则令 i 不变,j = next[j]    
			//next[j]即为j所对应的next值      
			j = next[j];
		}
	}
	if (j == pLen)
		return i - j;
	else
		return -1;
}


在做字符串题目的时候可能出现的情况比较多,要注意数组越界的问题啊,空字符串啊,还有一些乱七八糟的特殊情况。

分治:

分治算法,分而治之嘛,就是把一个大问题去化成一个一个的小问题,而一个一个的小问题的解和起来之后,就是整个大问题的解。快排啊归并啊,也都是利用的分治算法。

分治算法一般先要将一个大的问题,分解,不断分解为一个很容易得出结果的小问题;之后就是合并,将每个小问题的解进行合并,得出大问题的解。而且,分治算法所分出的小问题的解,应该是互相独立的,如果各个子问题的解之间有影响的话,就应该使用动态规划来解决了。


动态规划:

动态规划和分治算法思想上差不多,也是把大问题分成一个一个的子问题,通过子问题的解来得出整个问题的解。

但和分治算法的不同是分治算法得到的子问题的解互相独立,而动态规划得出的子问题的解之间并不独立,所以动态规划问题一般都会用一个数组来存储之前所得到的解,避免重复计算,提高效率,得出各个子问题的最优解,向上推导出上一项问题的最优解。


哈希表:

做的哈希表的题都不是很难,一般都是在看到题目里有要统计某个元素的出现次数之类的,或者不重复之类的,就考虑到了哈希表。

哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间;而打低价仅仅是消耗比较多的内存。


深度广度优先:

深度广度优先遍历,广度优先搜索依赖的是队列解决问题,深度优先搜索依赖的是递归,深度优先就是先访问它的子节点,子节点访问完之后访问邻近节点的子节点,直到遍历完;广度优先是先访问邻近节点,将邻近节点全部访问完之后去访问相应的子节点,直到访问完。

做的这种题也不多,而且也没有做很典型的题,所以。。以后熟悉了再补!


二分搜索:

一个排好序的数组,查找其中的元素,先找到最中间的数字,然后比较查找数字与中间数字的大小,相等返回,大于则继续查找后半边,小于则继续查找前半边,直到找到元素为止。贴一下比较核心的代码:

//二分搜索
    public static int search(int[] nums, int begin, int end, int target){

        if (begin >= end && target != nums[begin]) //查找不到的情况
            if (target > nums[end])//如果比查找的当前数字大,返回为当前数字索引+1
                return end + 1;
            else//查找的数字比当前数字小,返货为当前数字索引
                return end;

        //二分搜索
        int mid = (begin+end)/2;
        if (target == nums[mid])
            return mid;
        else if (target > nums[mid])
            return search(nums, mid+1 , end, target);
        else
            return search(nums, begin, mid-1, target);
    }

拓扑排序:

判断一个有向图是否是有环,重点在于维护一个入度为0的顶点集合,找到一个入度为0的顶点,接着删除掉它所连接的边,当入度为0的顶点集合为空时,如果还有边的存在,则证明着这个图中有环,如果没有了边,则证明这是一个无环图。

课程表 I II 都是拓扑排序的题目,贴一下LeetCode207的部分代码

//找出入度数为0的数字,保存到集合中
        for (int i = 0; i < numCourses; i++) {
            for (int j = 0; j < numCourses; j++) {
                if (pathin[j] == 0){//如果入度数为0 对应的map里的后继集合中的所有数字的pahtin--
                    result.add(j);//让入度为0的数字进入集合
                    pathin[j] = -1;//标记为已经查找过了
                    List<Integer> temp = map.get(j);
                    for (int k = 0; temp!=null && k < temp.size(); k++) {
                        pathin[temp.get(k)]--;
                    }
                }
            }

        }

        if (result.size()==numCourses)
            System.out.println("true");
        else
            System.out.println("false");

优先队列 堆:

堆分为大顶堆小顶堆,大顶堆父节点总是大于等于它的子节点,同样小顶堆则是父节点永远小于等于它的子节点。也就是说大顶堆的堆顶永远是堆中最大的数,小顶堆的堆顶永远是堆中最小的数。

java中优先队列PriorityQueue实现的就是堆,默认小顶堆,如果想要使用大顶堆的话,可以自己重写比较器。

可以用来找出前k个最大/小的数据,当数据量很大很大的时候,单纯的排序找出前k个数据就会显得很慢,使用堆排序的话就会快很多。


回溯算法:

回溯算法的定义:回溯算法也叫试探法,它是一种系统地搜索问题的解的方法,基本做法是深度优先搜索,是一种组织得井井有条的、能避免不必要重复搜索的穷举式搜索算法。回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。

回溯算法比较典型的题目,八皇后问题,LeetCode51贴下部分代码:

public static void solve(List<List<String>> list, List<String> temp, int n, String[] str,int[] nums,int j){
        if (j == n){
            count++;
            for (int i = 0; i < n; i++) {
                temp.add(str[nums[i]]);
            }
            list.add(new ArrayList<String>(temp));
            temp.removeAll(temp);
            return;
        }
        for (int i = 0; i < n; i++) {
            nums[j] = i;
            if (isOk(nums,j)){
                solve(list,temp,n,str,nums,j+1);
            }
        }

    }

    public static boolean isOk(int[] nums, int j){
        for (int i = j-1; i >= 0 ; i--) {
            if (nums[i] == nums[j] || nums[i] - nums[j] == i - j || nums[i] - nums[j] == j - i){
                return false;
            }
        }
        return true;
    }

还有全排列问题,LeetCode47,带重复元素的全排列,部分代码:

public static void allArray(List<List<Integer>> list, List<Integer> temp, int begin, int end) {
        if (begin == end)
            list.add(new ArrayList<Integer>(temp));

        for (int i = begin; i < temp.size(); ++i) {
            if (isSame(temp, begin, i)) {
                swap(temp, begin, i);
                allArray(list, temp, begin + 1, end);
                swap(temp, begin, i);
            }
        }
    }

    public static boolean isSame(List<Integer> temp, int begin, int end){
        for (int i = begin; i < end; i++)
            if (temp.get(i) == temp.get(end))
                return false;
        return true;
    }

树:

树的先序遍历,中序遍历,后序遍历:

    public void iterater(TreeNode root, List<Integer> list){

        list.add(root.val);
        if (root.left != null)
            iterater(root.left,list);
        if(root.right != null)
            iterater(root.right,list);
    }

public void iterater(TreeNode root, List<Integer> list){
        if (root.left != null)
            iterater(root.left,list);
        list.add(root.val);
        if(root.right != null)
            iterater(root.right,list);
    }
    public void iterater(TreeNode root, List<Integer> list){
        if (root.left != null)
            iterater(root.left,list);
        if(root.right != null)
            iterater(root.right,list);
        list.add(root.val);
    }

并查集:

并查集是有一个数组pre[],和两个函数构成的,一个函数为find()函数,用于寻找前导点的,第二个函数是join()用于合并路线的。路径压缩为了加快查找速度,将x点与其根节点直接相连,构造成类似于只有叶子节点而没有分直接点的树。

并查集也并没有怎么用到,而且一直觉得很迷,每次做这种题都要看一遍并查集,感觉像是理解了,可实际并没有。。。。。。

主要就是贴一下那两个函数的模板,网上找来哒:

void join(int x,int y)
{
    int a=find(x);//x的根节点为a
    int b=find(y);//y的根节点为b
    if(a!=b)//如果a,b不是相同的根节点,则说明ab不是连通的
    {
        pre[a]=b;//我们将ab相连 将a的前导结点设置为b
    }
}
int find(int x)
{
    int r=x;
    while(pre[r]!=r)
    r=pre[r];//找到他的前导结点
    int i=x,j;
    while(i!=r)//路径压缩算法
    {
        j=pre[i];//记录x的前导结点
        pre[i]=r;//将i的前导结点设置为r根节点
        i=j;
    }
    return r;
}

贪心算法:

贪心算法每次得到的解是当前的最优解,但并不一定是全局的最优解,比如找零问题,背包问题,都适合用贪心算法,还有LeetCode455分发饼干。

数学:

嗯。。就是想把那个质数的题再贴一下,LeetCode204部分代码:

boolean flag[] = new boolean[n];//初始全都false

        for (int i = 2; i < n ; i++) {
            if (flag[i] == false){
                count ++;
                for (int j = 1; j * i < n ; j++) {
                    flag[j * i] = true;
                }
            }
        }



嗯。。。暂时也就这些啦,等以后再慢慢补充,加油~






  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值