前言
这几天在看LeetCode,发现了 螺旋矩阵Ⅱ 这道标记为中等难度的题,如下所示。
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
示例 输入: n = 3 输出: [[1,2,3],[8,9,4],[7,6,5]]
但它并没有涉及到什么算法,就是一个简单的模拟过程。 从初始位置即矩阵Matrix[0][0] 开始填充数字,初始方向向右,控制列向量变化。然后再判断下一个位置即Matrix[0][1] 是否为边界或已经被填充,若否,则继续填充,若是,则转换方向,控制行向量变化。之后反复操作即可。
题解如下:
var generateMatrix = function(n) {
let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
let maxNum = n * n
let nowNum = 1;
let currentX = 0,currentY = 0;
let directions = [[0,1],[1,0],[0,-1],[-1,0]]
let moveIndex = 0
while(nowNum <= maxNum){
res[currentX][currentY] = nowNum ++
let nextX = currentX + directions[moveIndex][0]
let nextY = currentY + directions[moveIndex][1]
if(nextX >= n || nextY >= n || nextX < 0 || nextY < 0 || res[nextX][nextY]!=0)
moveIndex = (moveIndex + 1 ) % 4
currentX = currentX + directions[moveIndex][0]
currentY = currentY + directions[moveIndex][1]
}
return res
};
当然,今天并不是来分享这道题是怎么解的。我还用HTML+CSS+JS把刚才的过程 “可视化” 了。
思路
- 输入矩阵维度
n 。 - 根据算法生成结果矩阵即数组
resMatrix ,并将每一步的结果保存到resObj 对象中。 - 使用Grid布局将
wrap 分割成n * n的大小相同的网格。 - 在
wrap 中生成n * n个box ,将其透明度设为0,并与resMatrix 中的值一一对应。 index 初始值为1,使用setInterval 控制index ,若box 的文本值等于index ,则将box 显示出来,设置对应的背景颜色,并添加简单的动画效果。resObj 使得同一个方向中的box 背景颜色相同。
实现效果
GIF工具 ScreenToGif
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>螺旋矩阵Ⅱ</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
width: 100vw;
height: 100vh;
background-color: #bdc3c7;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
input {
width: 500px;
padding: 20px;
font-size: 20px;
box-sizing: border-box;
border: none;
outline: none;
}
.main {
margin-top: 50px;
display: flex;
justify-content: center;
position: relative;
}
.wrap {
width: 500px;
height: 500px;
display: grid;
gap: 5px;
position: relative;
overflow: hidden;
}
.wrap-trans::before {
content: "";
position: absolute;
width: 50px;
height: 100%;
background: linear-gradient(0deg,rgba(255,255,255,0),rgba(255,255,255,0.5),rgba(255,255,255,0));
z-index: 2;
animation: twinkle .5s forwards;
}
.message {
width: 300px;
max-height: 500px;
overflow-y: scroll;
box-sizing: border-box;
padding: 0 30px;
position: absolute;
right: -300px;
color: #34495e;
font-size: 12px;
line-height: 30px;
}
.message::-webkit-scrollbar{
width: 5px;
height: 1px;
}
.message::-webkit-scrollbar-thumb{
border-radius: 4px;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background: #bdc3c7;
}
.box {
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
font-size: 24px;
color: #34495e;
font-weight: bold;
}
.showAnimation {
animation-name: showBox;
animation-duration: .5s;
animation-fill-mode: forwards;
}
@keyframes showBox {
from{
opacity: 0;
}
to{
opacity: 1;
}
}
@keyframes twinkle {
from{
transform: translateX(0);
}
to{
transform: translateX(520px);
}
}
</style>
</head>
<body>
<input type="text" id="inputEl" placeholder="请输入矩阵维度(1-10)...">
<div class="main">
<div class="wrap"></div>
<div class="message"></div>
</div>
<script>
const DELAY = 100
const colors = ['#1abc9c','#f1c40f','#e84393','#e67e22','#3498db','#e74c3c','#9b59b6','#fab1a0','#95a5a6']
let resMatrix;
let resObj
inputEl.onkeyup = function(e){
var e = e || window.event
if(e.keyCode == 13){
let n = parseInt(inputEl.value)
if(n >= 1 && n <= 10){
let wrap = document.querySelector('.wrap')
resMatrix = createMatrix(n)
clearDom(wrap)
updateGrid(wrap,n)
createDom(wrap,n)
showBox(n)
}else
alert('请输入1-10之间的整数')
}
}
function updateGrid(dom,n){
dom.style.gridTemplateRows = `repeat(${n},1fr)`
dom.style.gridTemplateColumns = `repeat(${n},1fr)`
}
function clearDom(dom){
while(dom.hasChildNodes()){
dom.removeChild(dom.firstChild);
}
}
function createDom(dom,n){
for(let i=0;i<n;i++){
for(let j=0;j<n;j++){
let box = document.createElement('div')
box.innerHTML = resMatrix[i][j]
box.classList.add('box')
box.style.opacity = '0'
dom.appendChild(box)
}
}
}
function showBox(n){
let boxArray = Array.from(document.querySelectorAll('.box'))
let messageBox = document.querySelector('.message')
let index = 1
messageBox.innerText = ""
let timer = setInterval(() => {
if(index == n*n){
clearInterval(timer);
document.querySelector('.wrap').classList.add('wrap-trans')
setTimeout(() => {
document.querySelector('.wrap').classList.remove('wrap-trans')
}, 1000);
}
let nowBox = boxArray.filter(item => {
return item.innerHTML == index
})
nowBox[0].classList.add('showAnimation')
nowBox[0].style.background = colors[resObj[index] % colors.length]
messageBox.innerText += `正在填充${index},调整方向${resObj[index]}次。\n`
messageBox.scrollTop = messageBox.scrollHeight;
index ++
}, DELAY);
}
function createMatrix(n){
let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
resObj = {}
let maxNum = n * n
let nowNum = 1;
let currentX = 0,currentY = 0;
let directions = [[0,1],[1,0],[0,-1],[-1,0]]
let moveIndex = 0
let step = 0
while(nowNum <= maxNum){
res[currentX][currentY] = nowNum ++
let nextX = currentX + directions[moveIndex][0]
let nextY = currentY + directions[moveIndex][1]
if(nextX >= n || nextY >= n || nextX < 0 || nextY < 0 || res[nextX][nextY]!=0){
moveIndex = (moveIndex + 1 ) % 4
step ++
resObj[nowNum-1] = step
}else{
resObj[nowNum-1] = step
}
currentX = currentX + directions[moveIndex][0]
currentY = currentY + directions[moveIndex][1]
}
return res
}
</script>
</body>
</html>
结语
上面实现的是先一次性生成所有的box 并隐藏起来,通过定时器再显示出来。但在写的时候,由于一开始没有控制维度的大小,不小心输入了123,导致页面直接卡住了😂
那其实还有另外一种想法——逐个生成box ,轮到你了你再来。但这样的结果是,由于box 是按照顺序排放的,第一行排完了,直接从第二行第一列开始,这样就没法达到想要的效果,当然好像也可以通过grid-area 来为每一个box 固定位置,这可能也是一种思路,但我设置的最大维度为10,这样一来在速度上两者可能也没有多大差别哈哈。
|