LeetCode 289 - 生命游戏 - 原地算法

Wikipedia关于原地算法的描述:原地算法(in-place algorithm)基本上不需要额外辅助的数据结构,然而,允许少量额外的辅助变量来转换数据的算法。当算法运行时,输入的数据通常会被要输出的部分覆盖掉

很容易看出,原地算法的特点是不需要辅助的数据结构而只需要辅助变量。通常,维护一个复杂的数据结构时间空间复杂度都是比较高的,原地算法的优越性正来自于不依赖数据结构,尽管它借鉴了数据结构的思想。

同时也不难看出,原地算法总体的实现思路就是用输出的数据在原地覆盖已经“没用”的输入的数据

先做的是一条简单题:LeetCode 1047 删除字符串中所有相邻重复项

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

 

示例:

输入:"abbaca"
输出:"ca"
解释:
例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。

方法一:最容易想到的就是维护一个栈,因为本题删除字符的操作具有即时性。可以理解为“从第一个字符开始读字符串,遇到相同的字符立刻把它们从字符串中删除”,这个删除是即时生效的,会影响到后续的操作。比如“abbbaca”经过删除后不会变成“ca”,而是会变成“abaca”:在读到第三个“b”时前两个“b”已经被删去,所以不构成重复。读取字符并和前面的字符比较,如果相同则把前面的字符删去,这符合栈后进先出的特性。事实上,最后留在栈中的字符串就是结果。

 1 class Solution {
 2 public:
 3     string removeDuplicates(string S) {
 4         stack<char> s;
 5         s.push(S[0]);
 6         for(int i=1;i!=S.length();++i)
 7         {
 8             if(!s.empty())
 9             {
10                 if(s.top()==S[i])
11                     s.pop();
12                 else
13                     s.push(S[i]);
14             }
15             else
16             {
17                 s.push(S[i]);
18             }
19         }
20         string res = "";
21         while (!s.empty())
22         {
23             res += s.top();
24             s.pop();
25         }
26         reverse(res.begin(),res.end());
27         return res;
28     }
29 };

维护一个栈是LeetCode官方给出的题解,也是最容易想到的,但只超过了60%的提交,在评论区找到了原地算法这个名词。

方法二:原地算法。原地算法借助一个辅助变量len来确定最终的经过删减的字符串的长度。并且在算法运行过程中,直接将删减后的字符赋给相应的位置。以题给的字符串“abbaca”为例,在算法运行过程中,它的变化情况是这样的:abbaca(a赋给S[0],len=1)->abbaca(b!=a,len=2)->abbaca(b==b,len=1)->abbaca(a==a,len=0)->cbbaca(c赋给S[0],len=1)->cabaca(c!=a,len=2)->ca(截取长度为len的字符串)。在读取到第二个a时,这时候实质上前面的字符已经确定要被删除,因此可以被覆盖。

 1 class Solution {
 2 public:
 3     string removeDuplicates(string S) {
 4         int len = 0;
 5         for (char ch : S) {
 6             if (len == 0 || S[len - 1] != ch) {
 7                 S[len] = ch;
 8                 ++len;
 9             } else {
10                 len--;
11             }
12         }
13         S.resize(len);
14         return S;
15     }
16 };

方法三:还有一种方式也颇具原地算法的精神,即维护一个栈,而不实际构造一个栈,把字符串本身当作栈来用。顺序栈本身就是依靠数组实现的,因此在一个字符数组中模拟栈的操作并不违和。这样可以避免额外开辟栈的空间浪费。

 1 class Solution {
 2 public:
 3     string removeDuplicates(string S) {
 4         char top;
 5         string res="";
 6         res+=S[0];
 7         top=S[0];
 8         for(int i=1;i!=S.length();++i)
 9         {
10             if(top==S[i])
11             {
12                 res.erase(res.end()-1);
13                 if(res!="")
14                     top=res[res.length()-1];
15                 else if(res=="")
16                     top=‘ ‘;
17             }
18             else if(top!=S[i])
19             {
20                 res+=S[i];
21                 top=S[i];
22             }
23         }
24         return res;
25     }
26 };

再看一道难一点的:LeetCode 289 生命游戏

根据百度百科,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。

给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞具有一个初始状态 live(1)即为活细胞, 或 dead(0)即为死细胞。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:

如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
根据当前状态,写一个函数来计算面板上细胞的下一个(一次更新后的)状态。下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。

示例:

输入:
[
  [0,1,0],
  [0,0,1],
  [1,1,1],
  [0,0,0]
]
输出:
[
  [0,0,0],
  [1,0,1],
  [0,1,1],
  [0,1,0]
]

方法一:看一眼就能想到的方法是,扫描一遍二维数组,建立一个标记数组记录细胞的死活,然后再遍历一遍改变细胞的状态,很简单也很浪费空间的方法。

 1 class Solution {
 2 public:
 3     void gameOfLife(vector<vector<int>>& board) {
 4         vector<vector<bool>> res;
 5         int m = board.size();
 6         int n = board[0].size();
 7         for (int i = 0; i < m; ++i)
 8         {
 9             vector<bool> temp_res;
10             bool flag;
11             for (int j = 0; j < n; ++j)
12             {
13                 int count = 0;
14                 //上
15                 if (i > 0)
16                     count += board[i - 1][j];
17                 //下
18                 if (i < m - 1)
19                     count += board[i + 1][j];
20                 //左
21                 if (j > 0)
22                     count += board[i][j - 1];
23                 //右
24                 if (j < n - 1)
25                     count += board[i][j + 1];
26                 //左上
27                 if (i > 0 && j > 0)
28                     count += board[i - 1][j - 1];
29                 //右上
30                 if (i > 0 && j < n-1)
31                     count += board[i - 1][j + 1];
32                 //左下
33                 if (i < m - 1 && j>0)
34                     count += board[i + 1][j - 1];
35                 //右下
36                 if (i<m-1 && j<n-1)
37                     count += board[i + 1][j + 1];
38
39                 if (2 > count)
40                     flag = 0;
41                 if (2 == count)
42                 {
43                     if (board[i][j] == 0)
44                         flag = 0;
45                     else if(board[i][j]==1)
46                         flag = 1;
47                 }
48                 if (count==3)
49                     flag = 1;
50                 if (3 < count)
51                     flag = 0;
52                 temp_res.push_back(flag);
53             }
54             res.push_back(temp_res);
55         }
56         for (int i = 0; i != res.size(); ++i)
57         {
58             for (int j = 0; j != res[0].size(); ++j)
59             {
60                 if (0 == res[i][j])
61                     board[i][j] = 0;
62                 else if (1 == res[i][j])
63                     board[i][j] = 1;
64             }
65         }
66     }
67 };

方法二:原地算法。本题不能再原数组改变细胞状态的原因是细胞的生死是同时发生的,也就是说局部的改变不会影响到后面的细胞。用标记数组似乎是理所当然的做法,但是题目并没有限定为布尔数组,完全可以用其他数据来表示细胞的状态(或者说一种趋势)而同时不影响后续的判定。

 1 void gameOfLife(vector<vector<int>>& board)
 2     {
 3         for(int i=0;i<board.size();i++)
 4             for(int j=0;j<board[0].size();j++)
 5                 f(board,i,j);
 6         for(int i=0;i<board.size();i++)
 7             for(int j=0;j<board[0].size();j++)
 8             {
 9                 if(board[i][j] == 2)
10                     board[i][j] = 0;
11                 if(board[i][j] == -1)
12                     board[i][j] = 1;
13             }
14
15     }
16     void f(vector<vector<int>>& board,int x,int y)
17     {
18         int live = 0;
19         if(x>0)
20         {
21             if(board[x-1][y]>0)
22                 live++;
23             if(y>0 && board[x-1][y-1]>0)
24                 live++;
25             if(y<board[0].size()-1 && board[x-1][y+1]>0)
26                 live++;
27         }
28         if(x<board.size()-1)
29         {
30             if(board[x+1][y]>0)
31                 live++;
32             if(y>0 && board[x+1][y-1]>0)
33                 live++;
34             if(y<board[0].size()-1 && board[x+1][y+1]>0)
35                 live++;
36         }
37         if(y >0 && board[x][y-1]>0)
38             live++;
39         if(y < board[0].size()-1 && board[x][y+1]>0)
40             live++;
41         if(board[x][y] == 1)
42         {
43             if(live<2||live>3)
44                 board[x][y] = 2;
45         }
46         else if(live == 3)
47             board[x][y] = -1;
48     }

原文地址:https://www.cnblogs.com/CofJus/p/11386155.html

时间: 2024-11-13 13:31:08

LeetCode 289 - 生命游戏 - 原地算法的相关文章

Leetcode 289.生命游戏

生命游戏 根据百度百科,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机. 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞.每个细胞具有一个初始状态 live(1)即为活细胞, 或 dead(0)即为死细胞.每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律: 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡: 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活: 如果活细胞周围八个位置有超过

[Leetcode] 第289题 生命游戏

一.题目描述 根据百度百科,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机. 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞.每个细胞具有一个初始状态 live(1)即为活细胞, 或 dead(0)即为死细胞.每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律: 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡: 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活: 如果活细胞周围八个位置有

LeetCode:Game of Life - 康威生命游戏

1.题目名称 Game of Life(康威生命游戏) 2.题目地址 https://leetcode.com/problems/game-of-life 3.题目内容 英文: According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conw

生命游戏 Java

本程序由四个类组成: 其中Init_data,用于初始化各个活细胞的状态judge_state,用于判断下一代的细胞状态,并进行更新.set_color,用于给GUI界面中各个细胞涂色set_frame,用于设置GUI界面的布局 1 /*初始化细胞状态类 2 * 四个模式 3 * 1.easy模式,一个大写的I 4 * 2.love模式,一个大写的LOVE 5 * 3.arrow模式,一个粗箭头 6 * 4.random模式,根据随机函数生成活细胞数 7 */ 8 package game_li

生命游戏/Game of Life的Java实现(转)

首先简单介绍一下<生命游戏> 生命游戏其实是一个零玩家游戏.它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞.一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量.如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去:相反,如果周围活细胞过少,这个细胞会因太孤单而死去.具体如下图: 每个格子的生死遵循下面的原则: 1. 如果一个细胞周围有3个细胞为生(一个细胞周围共有8个细胞),则该细胞为生(即该细胞若原先为死,则转为生,若原先为生,

Leetcode分类解析:组合算法

Leetcode分类解析:组合算法 所谓组合算法就是指:在解决一些算法问题时,需要产生输入数据的各种组合.排列.子集.分区等等,然后逐一确认每种是不是我们要的解.从广义上来说,组合算法可以包罗万象,甚至排序.各种搜索算法都可以算进去.最近读<The Algorithm Design Manual>时了解到这种归类,上网一查,甚至有专门的书籍讲解,而且Knuth的巨著TAOCP的第四卷就叫组合算法,看来还真是孤陋寡闻了!于是最近着重专攻了一下Leetcode中所有相关题目,在此整理一下学习心得.

C语言练习题一道——生命游戏

生命游戏是一个很简单,但却是很有趣的程序习题.在一个四周都可以延伸到无限的 棋盘上的某个格子中会有一个有机体.每一个有机体在时间t 时,会依照环绕着它的8 个邻 居的特性而决定在时间t+1 时是否能生存下去.如果某一格在时间t 时: (1)有一个有机体,但是它的邻居少于或等于1 个,或者是大于3 个,那就会因为不 够稠密或太过稠密,这个有机体在时间t+1 时就会死亡:换言之,在t+1 时间,那一格中不 会存在有机体.下面就是几个在时间t+1 时会死亡的例子,如图1 所示. 图1 (2)有有机体在

leetcode 174. 地下城游戏 解题报告

leetcode 174. 地下城游戏 一些恶魔抓住了公主(P)并将她关在了地下城的右下角.地下城是由 M x N 个房间组成的二维网格.我们英勇的骑士(K)最初被安置在左上角的房间里,他必须穿过地下城并通过对抗恶魔来拯救公主. 骑士的初始健康点数为一个正整数.如果他的健康点数在某一时刻降至 0 或以下,他会立即死亡. 有些房间由恶魔守卫,因此骑士在进入这些房间时会失去健康点数(若房间里的值为负整数,则表示骑士将损失健康点数):其他房间要么是空的(房间里的值为 0),要么包含增加骑士健康点数的魔

【转载】【元胞自动机】生命游戏(时间简史)

原帖:http://www.cnblogs.com/lvpengms/archive/2010/02/08/1666095.html 晚上闲着没事干,鉴于前一段时间数学建模中看到了生命游戏 Game of Life,所以用C++实现一下,效果还不错. 本程序是用vs2005 C++编写,有意想要程序和源码的朋友可以给我发邮件. 更新:程序源码可以从这个地址下载:下载元胞自动机源码及程序 =======================================================